<commit_before>use super::{heap::Pointer, Heap};
use crate::{
    compiler::{ast_to_hir::AstToHir, cst::CstDb, hir::Id},
    database::Database,
    language_server::utils::LspPositionConversion,
    module::Module,
};
use itertools::Itertools;

#[derive(Default, Clone)]
pub struct Tracer {
    log: Vec<TraceEntry>,
    stack: Vec<TraceEntry>,
}
#[derive(Clone)]
pub enum TraceEntry {
    ValueEvaluated {
        id: Id,
        value: Pointer,
    },
    CallStarted {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    CallEnded {
        return_value: Pointer,
    },
    NeedsStarted {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    NeedsEnded,
    ModuleStarted {
        module: Module,
    },
    ModuleEnded {
        export_map: Pointer,
    },
}

impl Tracer {
    pub fn push(&mut self, entry: TraceEntry) {
        self.log.push(entry.clone());
        match entry {
            TraceEntry::CallStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::CallEnded { .. } => {
                self.stack.pop().unwrap();
            }
            TraceEntry::NeedsStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::NeedsEnded => {
                self.stack.pop().unwrap();
            }
            TraceEntry::ModuleStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::ModuleEnded { .. } => {
                self.stack.pop().unwrap();
            }
            _ => {}
        }
    }
    pub fn log(&self) -> &[TraceEntry] {
        &self.log
    }

    pub fn stack(&self) -> &[TraceEntry] {
        &self.stack
    }
    pub fn dump_stack_trace(&self, db: &Database, heap: &Heap) {
        for line in self.format_stack_trace(db, heap).lines() {
            log::error!("{}", line);
        }
    }
    pub fn format_stack_trace(&self, db: &Database, heap: &Heap) -> String {
        // TODO: Format values properly.
        self.stack
            .iter()
            .rev()
            .map(|entry| {
                let (call_string, hir_id) = match entry {
                    TraceEntry::CallStarted { id, closure, args } => (
                        format!(
                            "{closure} {}",
                            args.iter().map(|arg| arg.format(heap)).join(" ")
                        ),
                        Some(id),
                    ),
                    TraceEntry::NeedsStarted {
                        id,
                        condition,
                        reason,
                    } => (
                        format!("needs {} {}", condition.format(heap), reason.format(heap)),
                        Some(id),
                    ),
                    TraceEntry::ModuleStarted { module } => (format!("module {module}"), None),
                    _ => unreachable!(),
                };
                let caller_location_string = {
                    let (hir_id, ast_id, cst_id, span) = if let Some(hir_id) = hir_id {
                        let module = hir_id.module.clone();
                        let ast_id = db.hir_to_ast_id(hir_id.clone());
                        let cst_id = db.hir_to_cst_id(hir_id.clone());
                        let cst = cst_id.map(|id| db.find_cst(module.clone(), id));
                        let span = cst.map(|cst| {
                            (
                                db.offset_to_lsp(module.clone(), cst.span.start),
                                db.offset_to_lsp(module.clone(), cst.span.end),
                            )
                        });
                        (Some(hir_id), ast_id, cst_id, span)
                    } else {
                        (None, None, None, None)
                    };
                    format!(
                        "{}, {}, {}, {}",
                        hir_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no hir>".to_string()),
                        ast_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no ast>".to_string()),
                        cst_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no cst>".to_string()),
                        span.map(|((start_line, start_col), (end_line, end_col))| format!(
                            "{}:{} – {}:{}",
                            start_line, start_col, end_line, end_col
                        ))
                        .unwrap_or_else(|| "<no location>".to_string())
                    )
                };
                format!("{caller_location_string:90} {call_string}")
            })
            .join("\n")
    }

    pub fn format_call_tree(&self, heap: &Heap) -> String {
        // TODO: Format values properly.
        let actions = self
            .log
            .iter()
            .map(|entry| match entry {
                TraceEntry::ValueEvaluated { id, value } => {
                    Action::Stay(format!("{id} = {}", value.format(heap)))
                }
                TraceEntry::CallStarted { id, closure, args } => Action::Start(format!(
                    "{id} {} {}",
                    closure.format(heap),
                    args.iter().map(|arg| arg.format(heap)).join(" ")
                )),
                TraceEntry::CallEnded { return_value } => {
                    Action::End(format!(" = {}", return_value.format(heap)))
                }
                TraceEntry::NeedsStarted {
                    id,
                    condition,
                    reason,
                } => Action::Start(format!(
                    "{id} needs {} {}",
                    condition.format(heap),
                    reason.format(heap)
                )),
                TraceEntry::NeedsEnded => Action::End(" = Nothing".to_string()),
                TraceEntry::ModuleStarted { module } => Action::Start(format!("module {module}")),
                TraceEntry::ModuleEnded { export_map } => Action::End(export_map.format(heap)),
            })
            .collect_vec();

        let mut lines = vec![];
        let mut stack = vec![];
        let mut indentation = 0;

        for action in actions {
            let indent = "  ".repeat(indentation);
            match action {
                Action::Start(line) => {
                    stack.push(lines.len());
                    lines.push(format!("{indent}{line}"));
                    indentation += 1;
                }
                Action::End(addendum) => {
                    let start = stack.pop().unwrap();
                    lines[start].push_str(&addendum);
                    indentation -= 1;
                }
                Action::Stay(line) => {
                    lines.push(format!("{indent}{line}"));
                }
            }
        }

        lines.join("\n")
    }
}

enum Action {
    Start(String),
    End(String),
    Stay(String),
}
<commit_msg>Remove unnecessary comments<commit_after>use super::{heap::Pointer, Heap};
use crate::{
    compiler::{ast_to_hir::AstToHir, cst::CstDb, hir::Id},
    database::Database,
    language_server::utils::LspPositionConversion,
    module::Module,
};
use itertools::Itertools;

#[derive(Default, Clone)]
pub struct Tracer {
    log: Vec<TraceEntry>,
    stack: Vec<TraceEntry>,
}
#[derive(Clone)]
pub enum TraceEntry {
    ValueEvaluated {
        id: Id,
        value: Pointer,
    },
    CallStarted {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    CallEnded {
        return_value: Pointer,
    },
    NeedsStarted {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    NeedsEnded,
    ModuleStarted {
        module: Module,
    },
    ModuleEnded {
        export_map: Pointer,
    },
}

impl Tracer {
    pub fn push(&mut self, entry: TraceEntry) {
        self.log.push(entry.clone());
        match entry {
            TraceEntry::CallStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::CallEnded { .. } => {
                self.stack.pop().unwrap();
            }
            TraceEntry::NeedsStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::NeedsEnded => {
                self.stack.pop().unwrap();
            }
            TraceEntry::ModuleStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::ModuleEnded { .. } => {
                self.stack.pop().unwrap();
            }
            _ => {}
        }
    }
    pub fn log(&self) -> &[TraceEntry] {
        &self.log
    }

    pub fn stack(&self) -> &[TraceEntry] {
        &self.stack
    }
    pub fn dump_stack_trace(&self, db: &Database, heap: &Heap) {
        for line in self.format_stack_trace(db, heap).lines() {
            log::error!("{}", line);
        }
    }
    pub fn format_stack_trace(&self, db: &Database, heap: &Heap) -> String {
        self.stack
            .iter()
            .rev()
            .map(|entry| {
                let (call_string, hir_id) = match entry {
                    TraceEntry::CallStarted { id, closure, args } => (
                        format!(
                            "{closure} {}",
                            args.iter().map(|arg| arg.format(heap)).join(" ")
                        ),
                        Some(id),
                    ),
                    TraceEntry::NeedsStarted {
                        id,
                        condition,
                        reason,
                    } => (
                        format!("needs {} {}", condition.format(heap), reason.format(heap)),
                        Some(id),
                    ),
                    TraceEntry::ModuleStarted { module } => (format!("module {module}"), None),
                    _ => unreachable!(),
                };
                let caller_location_string = {
                    let (hir_id, ast_id, cst_id, span) = if let Some(hir_id) = hir_id {
                        let module = hir_id.module.clone();
                        let ast_id = db.hir_to_ast_id(hir_id.clone());
                        let cst_id = db.hir_to_cst_id(hir_id.clone());
                        let cst = cst_id.map(|id| db.find_cst(module.clone(), id));
                        let span = cst.map(|cst| {
                            (
                                db.offset_to_lsp(module.clone(), cst.span.start),
                                db.offset_to_lsp(module.clone(), cst.span.end),
                            )
                        });
                        (Some(hir_id), ast_id, cst_id, span)
                    } else {
                        (None, None, None, None)
                    };
                    format!(
                        "{}, {}, {}, {}",
                        hir_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no hir>".to_string()),
                        ast_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no ast>".to_string()),
                        cst_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no cst>".to_string()),
                        span.map(|((start_line, start_col), (end_line, end_col))| format!(
                            "{}:{} – {}:{}",
                            start_line, start_col, end_line, end_col
                        ))
                        .unwrap_or_else(|| "<no location>".to_string())
                    )
                };
                format!("{caller_location_string:90} {call_string}")
            })
            .join("\n")
    }

    pub fn format_call_tree(&self, heap: &Heap) -> String {
        let actions = self
            .log
            .iter()
            .map(|entry| match entry {
                TraceEntry::ValueEvaluated { id, value } => {
                    Action::Stay(format!("{id} = {}", value.format(heap)))
                }
                TraceEntry::CallStarted { id, closure, args } => Action::Start(format!(
                    "{id} {} {}",
                    closure.format(heap),
                    args.iter().map(|arg| arg.format(heap)).join(" ")
                )),
                TraceEntry::CallEnded { return_value } => {
                    Action::End(format!(" = {}", return_value.format(heap)))
                }
                TraceEntry::NeedsStarted {
                    id,
                    condition,
                    reason,
                } => Action::Start(format!(
                    "{id} needs {} {}",
                    condition.format(heap),
                    reason.format(heap)
                )),
                TraceEntry::NeedsEnded => Action::End(" = Nothing".to_string()),
                TraceEntry::ModuleStarted { module } => Action::Start(format!("module {module}")),
                TraceEntry::ModuleEnded { export_map } => Action::End(export_map.format(heap)),
            })
            .collect_vec();

        let mut lines = vec![];
        let mut stack = vec![];
        let mut indentation = 0;

        for action in actions {
            let indent = "  ".repeat(indentation);
            match action {
                Action::Start(line) => {
                    stack.push(lines.len());
                    lines.push(format!("{indent}{line}"));
                    indentation += 1;
                }
                Action::End(addendum) => {
                    let start = stack.pop().unwrap();
                    lines[start].push_str(&addendum);
                    indentation -= 1;
                }
                Action::Stay(line) => {
                    lines.push(format!("{indent}{line}"));
                }
            }
        }

        lines.join("\n")
    }
}

enum Action {
    Start(String),
    End(String),
    Stay(String),
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Servo's compiler plugin/macro crate
//!
//! Attributes this crate provides:
//!
//!  - `#[privatize]` : Forces all fields in a struct/enum to be private
//!  - `#[jstraceable]` : Auto-derives an implementation of `JSTraceable` for a struct in the script crate
//!  - `#[must_root]` : Prevents data of the marked type from being used on the stack. See the lints module for more details
//!  - `#[dom_struct]` : Implies `#[privatize]`,`#[jstraceable]`, and `#[must_root]`.
//!     Use this for structs that correspond to a DOM type

#![feature(plugin_registrar, quote, plugin, box_syntax, rustc_private, core, unicode)]

#![allow(missing_copy_implementations)]

#[plugin]
#[macro_use]
extern crate syntax;
#[plugin]
#[macro_use]
extern crate rustc;

use rustc::lint::LintPassObject;
use rustc::plugin::Registry;
use syntax::ext::base::{Decorator, Modifier};

use syntax::parse::token::intern;

// Public for documentation to show up
/// Handles the auto-deriving for `#[jstraceable]`
pub mod jstraceable;
/// Autogenerates implementations of Reflectable on DOM structs
pub mod reflector;
pub mod lints;
/// Utilities for writing plugins
pub mod utils;
pub mod casing;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(intern("dom_struct"), Modifier(box jstraceable::expand_dom_struct));
    reg.register_syntax_extension(intern("jstraceable"), Decorator(box jstraceable::expand_jstraceable));
    reg.register_syntax_extension(intern("_generate_reflector"), Decorator(box reflector::expand_reflector));
    reg.register_macro("to_lower", casing::expand_lower);
    reg.register_macro("to_upper", casing::expand_upper);
    reg.register_lint_pass(box lints::transmute_type::TransmutePass as LintPassObject);
    reg.register_lint_pass(box lints::unrooted_must_root::UnrootedPass as LintPassObject);
    reg.register_lint_pass(box lints::privatize::PrivatizePass as LintPassObject);
    reg.register_lint_pass(box lints::inheritance_integrity::InheritancePass as LintPassObject);
    reg.register_lint_pass(box lints::str_to_string::StrToStringPass as LintPassObject);
    reg.register_lint_pass(box lints::ban::BanPass as LintPassObject);
}
<commit_msg>auto merge of #5201 : Ms2ger/servo/plugins, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Servo's compiler plugin/macro crate
//!
//! Attributes this crate provides:
//!
//!  - `#[privatize]` : Forces all fields in a struct/enum to be private
//!  - `#[jstraceable]` : Auto-derives an implementation of `JSTraceable` for a struct in the script crate
//!  - `#[must_root]` : Prevents data of the marked type from being used on the stack. See the lints module for more details
//!  - `#[dom_struct]` : Implies `#[privatize]`,`#[jstraceable]`, and `#[must_root]`.
//!     Use this for structs that correspond to a DOM type

#![feature(plugin_registrar, quote, plugin, box_syntax, rustc_private, core, unicode)]

#![allow(missing_copy_implementations)]

#[macro_use]
extern crate syntax;
#[macro_use]
extern crate rustc;

use rustc::lint::LintPassObject;
use rustc::plugin::Registry;
use syntax::ext::base::{Decorator, Modifier};

use syntax::parse::token::intern;

// Public for documentation to show up
/// Handles the auto-deriving for `#[jstraceable]`
pub mod jstraceable;
/// Autogenerates implementations of Reflectable on DOM structs
pub mod reflector;
pub mod lints;
/// Utilities for writing plugins
pub mod utils;
pub mod casing;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(intern("dom_struct"), Modifier(box jstraceable::expand_dom_struct));
    reg.register_syntax_extension(intern("jstraceable"), Decorator(box jstraceable::expand_jstraceable));
    reg.register_syntax_extension(intern("_generate_reflector"), Decorator(box reflector::expand_reflector));
    reg.register_macro("to_lower", casing::expand_lower);
    reg.register_macro("to_upper", casing::expand_upper);
    reg.register_lint_pass(box lints::transmute_type::TransmutePass as LintPassObject);
    reg.register_lint_pass(box lints::unrooted_must_root::UnrootedPass as LintPassObject);
    reg.register_lint_pass(box lints::privatize::PrivatizePass as LintPassObject);
    reg.register_lint_pass(box lints::inheritance_integrity::InheritancePass as LintPassObject);
    reg.register_lint_pass(box lints::str_to_string::StrToStringPass as LintPassObject);
    reg.register_lint_pass(box lints::ban::BanPass as LintPassObject);
}
<|endoftext|>


<commit_before>/// Parse the input TokenStream of a macro, triggering a compile error if the
/// tokens fail to parse.
///
/// Refer to the [`parse` module] documentation for more details about parsing
/// in Syn.
///
/// [`parse` module]: crate::rustdoc_workaround::parse_module
///
///

///
/// # Intended usage
///
/// This macro must be called from a function that returns
/// `proc_macro::TokenStream`. Usually this will be your proc macro entry point,
/// the function that has the #\[proc_macro\] / #\[proc_macro_derive\] /
/// #\[proc_macro_attribute\] attribute.
///
/// ```
/// # extern crate proc_macro;
/// #
/// use proc_macro::TokenStream;
/// use syn::{parse_macro_input, Result};
/// use syn::parse::{Parse, ParseStream};
///
/// struct MyMacroInput {
///     /* ... */
/// }
///
/// impl Parse for MyMacroInput {
///     fn parse(input: ParseStream) -> Result<Self> {
///         /* ... */
/// #       Ok(MyMacroInput {})
///     }
/// }
///
/// # const IGNORE: &str = stringify! {
/// #[proc_macro]
/// # };
/// pub fn my_macro(tokens: TokenStream) -> TokenStream {
///     let input = parse_macro_input!(tokens as MyMacroInput);
///
///     /* ... */
/// #   "".parse().unwrap()
/// }
/// ```
///
///

///
/// # Expansion
///
/// `parse_macro_input!($variable as $Type)` expands to something like:
///
/// ```no_run
/// # extern crate proc_macro;
/// #
/// # macro_rules! doc_test {
/// #     ($variable:ident as $Type:ty) => {
/// match syn::parse::<$Type>($variable) {
///     Ok(syntax_tree) => syntax_tree,
///     Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
/// }
/// #     };
/// # }
/// #
/// # fn test(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
/// #     let _ = doc_test!(input as syn::Ident);
/// #     proc_macro::TokenStream::new()
/// # }
/// ```
#[macro_export]
macro_rules! parse_macro_input {
    ($tokenstream:ident as $ty:ty) => {
        match $crate::parse_macro_input::parse::<$ty>($tokenstream) {
            $crate::export::Ok(data) => data,
            $crate::export::Err(err) => {
                return $crate::export::TokenStream::from(err.to_compile_error());
            }
        }
    };
    ($tokenstream:ident) => {
        $crate::parse_macro_input!($tokenstream as _)
    };
}

////////////////////////////////////////////////////////////////////////////////
// Can parse any type that implements Parse.

use crate::parse::{Parse, ParseStream, Parser, Result};
use proc_macro::TokenStream;

// Not public API.
#[doc(hidden)]
pub fn parse<T: ParseMacroInput>(token_stream: TokenStream) -> Result<T> {
    T::parse.parse(token_stream)
}

// Not public API.
#[doc(hidden)]
pub trait ParseMacroInput: Sized {
    fn parse(input: ParseStream) -> Result<Self>;
}

impl<T: Parse> ParseMacroInput for T {
    fn parse(input: ParseStream) -> Result<Self> {
        <T as Parse>::parse(input)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Any other types that we want `parse_macro_input!` to be able to parse.

#[cfg(any(feature = "full", feature = "derive"))]
use crate::AttributeArgs;

#[cfg(any(feature = "full", feature = "derive"))]
impl ParseMacroInput for AttributeArgs {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut metas = Vec::new();

        loop {
            if input.is_empty() {
                break;
            }
            let value = input.parse()?;
            metas.push(value);
            if input.is_empty() {
                break;
            }
            input.parse::<Token![,]>()?;
        }

        Ok(metas)
    }
}
<commit_msg>Add case to parse_macro_input! to use a Parser function<commit_after>/// Parse the input TokenStream of a macro, triggering a compile error if the
/// tokens fail to parse.
///
/// Refer to the [`parse` module] documentation for more details about parsing
/// in Syn.
///
/// [`parse` module]: crate::rustdoc_workaround::parse_module
///
///

///
/// # Intended usage
///
/// This macro must be called from a function that returns
/// `proc_macro::TokenStream`. Usually this will be your proc macro entry point,
/// the function that has the #\[proc_macro\] / #\[proc_macro_derive\] /
/// #\[proc_macro_attribute\] attribute.
///
/// ```
/// # extern crate proc_macro;
/// #
/// use proc_macro::TokenStream;
/// use syn::{parse_macro_input, Result};
/// use syn::parse::{Parse, ParseStream};
///
/// struct MyMacroInput {
///     /* ... */
/// }
///
/// impl Parse for MyMacroInput {
///     fn parse(input: ParseStream) -> Result<Self> {
///         /* ... */
/// #       Ok(MyMacroInput {})
///     }
/// }
///
/// # const IGNORE: &str = stringify! {
/// #[proc_macro]
/// # };
/// pub fn my_macro(tokens: TokenStream) -> TokenStream {
///     let input = parse_macro_input!(tokens as MyMacroInput);
///
///     /* ... */
/// #   "".parse().unwrap()
/// }
/// ```
///
///

///
/// # Usage with Parser
///
/// This macro can also be used with the [`Parser` trait] for types that have
/// multiple ways that they can be parsed.
///
/// [`Parser` trait]: crate::rustdoc_workaround::parse_module::Parser
///
/// ```
/// # extern crate proc_macro;
/// #
/// # use proc_macro::TokenStream;
/// # use syn::{parse_macro_input, Result};
/// # use syn::parse::ParseStream;
/// #
/// # struct MyMacroInput {}
/// #
/// impl MyMacroInput {
///     fn parse_alternate(input: ParseStream) -> Result<Self> {
///         /* ... */
/// #       Ok(MyMacroInput {})
///     }
/// }
///
/// # const IGNORE: &str = stringify! {
/// #[proc_macro]
/// # };
/// pub fn my_macro(tokens: TokenStream) -> TokenStream {
///     let input = parse_macro_input!(tokens with MyMacroInput::parse_alternate);
///
///     /* ... */
/// #   "".parse().unwrap()
/// }
/// ```
///
///

///
/// # Expansion
///
/// `parse_macro_input!($variable as $Type)` expands to something like:
///
/// ```no_run
/// # extern crate proc_macro;
/// #
/// # macro_rules! doc_test {
/// #     ($variable:ident as $Type:ty) => {
/// match syn::parse::<$Type>($variable) {
///     Ok(syntax_tree) => syntax_tree,
///     Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
/// }
/// #     };
/// # }
/// #
/// # fn test(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
/// #     let _ = doc_test!(input as syn::Ident);
/// #     proc_macro::TokenStream::new()
/// # }
/// ```
#[macro_export]
macro_rules! parse_macro_input {
    ($tokenstream:ident as $ty:ty) => {
        match $crate::parse_macro_input::parse::<$ty>($tokenstream) {
            $crate::export::Ok(data) => data,
            $crate::export::Err(err) => {
                return $crate::export::TokenStream::from(err.to_compile_error());
            }
        }
    };
    ($tokenstream:ident) => {
        $crate::parse_macro_input!($tokenstream as _)
    };
    ($tokenstream:ident with $parser:path) => {{
        use $crate::parse::Parser;
        match $parser.parse($tokenstream) {
            $crate::export::Ok(data) => data,
            $crate::export::Err(err) => {
                return $crate::export::TokenStream::from(err.to_compile_error());
            }
        }
    }};
}

////////////////////////////////////////////////////////////////////////////////
// Can parse any type that implements Parse.

use crate::parse::{Parse, ParseStream, Parser, Result};
use proc_macro::TokenStream;

// Not public API.
#[doc(hidden)]
pub fn parse<T: ParseMacroInput>(token_stream: TokenStream) -> Result<T> {
    T::parse.parse(token_stream)
}

// Not public API.
#[doc(hidden)]
pub trait ParseMacroInput: Sized {
    fn parse(input: ParseStream) -> Result<Self>;
}

impl<T: Parse> ParseMacroInput for T {
    fn parse(input: ParseStream) -> Result<Self> {
        <T as Parse>::parse(input)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Any other types that we want `parse_macro_input!` to be able to parse.

#[cfg(any(feature = "full", feature = "derive"))]
use crate::AttributeArgs;

#[cfg(any(feature = "full", feature = "derive"))]
impl ParseMacroInput for AttributeArgs {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut metas = Vec::new();

        loop {
            if input.is_empty() {
                break;
            }
            let value = input.parse()?;
            metas.push(value);
            if input.is_empty() {
                break;
            }
            input.parse::<Token![,]>()?;
        }

        Ok(metas)
    }
}
<|endoftext|>


<commit_before>use unicode_width::UnicodeWidthStr;
use std::io::{self, Write};
use std::iter::FromIterator;
use std::fmt::{self, Write as FmtWrite};

#[derive(Debug,Clone)]
pub enum Action {
    Insert { start: usize, text: Vec<char> },
    Remove { start: usize, text: Vec<char> },
    StartGroup,
    EndGroup,
}

impl Action {
    pub fn do_on(&self, buf: &mut Buffer) {
        match *self {
            Action::Insert { start, ref text } => buf.insert_raw(start, &text[..]),
            Action::Remove { start, ref text } => {
                buf.remove_raw(start, start + text.len());
            }
            Action::StartGroup | Action::EndGroup => {}
        }
    }

    pub fn undo(&self, buf: &mut Buffer) {
        match *self {
            Action::Insert { start, ref text } => {
                buf.remove_raw(start, start + text.len());
            }
            Action::Remove { start, ref text } => buf.insert_raw(start, &text[..]),
            Action::StartGroup | Action::EndGroup => {}
        }
    }
}

#[derive(Debug, Clone)]
pub struct Buffer {
    data: Vec<char>,
    actions: Vec<Action>,
    undone_actions: Vec<Action>,
}

impl From<Buffer> for String {
    fn from(buf: Buffer) -> Self {
        String::from_iter(buf.data)
    }
}

impl From<String> for Buffer {
    fn from(s: String) -> Self {
        Buffer::from_iter(s.chars())
    }
}

impl<'a> From<&'a str> for Buffer {
    fn from(s: &'a str) -> Self {
        Buffer::from_iter(s.chars())
    }
}

impl fmt::Display for Buffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for &c in &self.data {
            try!(f.write_char(c));
        }
        Ok(())
    }
}

impl FromIterator<char> for Buffer {
    fn from_iter<T: IntoIterator<Item = char>>(t: T) -> Self {
        Buffer {
            data: t.into_iter().collect(),
            actions: Vec::new(),
            undone_actions: Vec::new(),
        }
    }
}

impl Buffer {
    pub fn new() -> Self {
        Buffer {
            data: Vec::new(),
            actions: Vec::new(),
            undone_actions: Vec::new(),
        }
    }

    pub fn clear_actions(&mut self) {
        self.actions.clear();
        self.undone_actions.clear();
    }

    pub fn start_undo_group(&mut self) {
        self.actions.push(Action::StartGroup);
    }

    pub fn end_undo_group(&mut self) {
        self.actions.push(Action::EndGroup);
    }

    pub fn undo(&mut self) -> bool {
        use Action::*;

        let did = !self.actions.is_empty();
        let mut group_nest = 0;
        let mut group_count = 0;
        while let Some(act) = self.actions.pop() {
            act.undo(self);
            self.undone_actions.push(act.clone());
            match act {
                EndGroup => {
                    group_nest += 1;
                    group_count = 0;
                }
                StartGroup => group_nest -= 1,
                // count the actions in this group so we can ignore empty groups below
                _ => group_count += 1,
            }

            // if we aren't in a group, and the last group wasn't empty
            if group_nest == 0 && group_count > 0 {
                break;
            }
        }
        did
    }

    pub fn redo(&mut self) -> bool {
        use Action::*;

        let did = !self.undone_actions.is_empty();
        let mut group_nest = 0;
        let mut group_count = 0;
        while let Some(act) = self.undone_actions.pop() {
            act.do_on(self);
            self.actions.push(act.clone());
            match act {
                StartGroup => {
                    group_nest += 1;
                    group_count = 0;
                }
                EndGroup => group_nest -= 1,
                // count the actions in this group so we can ignore empty groups below
                _ => group_count += 1,
            }

            // if we aren't in a group, and the last group wasn't empty
            if group_nest == 0 && group_count > 0 {
                break;
            }
        }
        did
    }

    pub fn revert(&mut self) -> bool {
        if self.actions.len() == 0 {
            return false;
        }

        while self.undo() {}
        true
    }

    fn push_action(&mut self, act: Action) {
        self.actions.push(act);
        self.undone_actions.clear();
    }

    pub fn num_chars(&self) -> usize {
        self.data.len()
    }

    pub fn num_bytes(&self) -> usize {
        let s: String = self.clone().into();
        s.len()
    }

    pub fn width(&self) -> usize {
        self.range_width(0, self.num_chars())
    }

    pub fn char_before(&self, cursor: usize) -> Option<char> {
        if cursor == 0 {
            None
        } else {
            self.data.get(cursor - 1).cloned()
        }
    }

    pub fn char_after(&self, cursor: usize) -> Option<char> {
        self.data.get(cursor).cloned()
    }

    /// Returns the number of characters removed.
    pub fn remove(&mut self, start: usize, end: usize) -> usize {
        let s = self.remove_raw(start, end);
        let num_removed = s.len();
        let act = Action::Remove {
            start: start,
            text: s,
        };
        self.push_action(act);
        num_removed
    }

    pub fn insert(&mut self, start: usize, text: &[char]) {
        let act = Action::Insert {
            start: start,
            text: text.into(),
        };
        act.do_on(self);
        self.push_action(act);
    }

    pub fn insert_from_buffer(&mut self, other: &Buffer) {
        let start = self.data.len();
        self.insert(start, &other.data[start..])
    }

    pub fn range(&self, start: usize, end: usize) -> String {
        self.data[start..end].iter().cloned().collect()
    }

    pub fn range_chars(&self, start: usize, end: usize) -> Vec<char> {
        self.data[start..end].iter().cloned().collect()
    }

    pub fn range_width(&self, start: usize, end: usize) -> usize {
        self.range(start, end)[..].width()
    }

    pub fn chars(&self) -> ::std::slice::Iter<char> {
        self.data.iter()
    }

    pub fn truncate(&mut self, num: usize) {
        let end = self.data.len();
        self.remove(num, end);
    }

    pub fn print<W>(&self, out: &mut W) -> io::Result<()>
        where W: Write
    {
        let string: String = self.data.iter().cloned().collect();
        try!(out.write(string.as_bytes()));

        Ok(())
    }

    /// Takes other buffer, measures its length and prints this buffer from the point where
    /// the other stopped.
    /// Used to implement autosuggestions.
    pub fn print_rest<W>(&self, out: &mut W, after: usize) -> io::Result<usize>
        where W: Write
    {
        let string: String = self.data.iter().skip(after).cloned().collect();
        out.write(string.as_bytes())?;

        Ok(string.len())
    }

    fn remove_raw(&mut self, start: usize, end: usize) -> Vec<char> {
        self.data.drain(start..end).collect()
    }

    fn insert_raw(&mut self, start: usize, text: &[char]) {
        for (i, &c) in text.iter().enumerate() {
            self.data.insert(start + i, c)
        }
    }

    /// Check if the other buffer starts with the same content as this one.
    /// Used to implement autosuggestions.
    pub fn starts_with(&self, other: &Buffer) -> bool {
        let other_len = other.data.len();
        let self_len = self.data.len();
        if other.data.len() != 0 && self_len != other_len {
            let match_let = self.data
                .iter()
                .zip(&other.data)
                .take_while(|&(s, o)| *s == *o)
                .collect::<Vec<_>>()
                .len();
            match_let == other_len
        } else {
            false
        }
    }

    /// Return true if the buffer is empty.
    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_insert() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_truncate_empty() {
        let mut buf = Buffer::new();
        buf.truncate(0);
        assert_eq!(String::from(buf), "");
    }

    #[test]
    fn test_truncate_all() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.truncate(0);
        assert_eq!(String::from(buf), "");
    }

    #[test]
    fn test_truncate_end() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let end = buf.num_chars();
        buf.truncate(end);
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_truncate_part() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.truncate(3);
        assert_eq!(String::from(buf), "abc");
    }

    #[test]
    fn test_truncate_empty_undo() {
        let mut buf = Buffer::new();
        buf.truncate(0);
        buf.undo();
        assert_eq!(String::from(buf), "");
    }

    #[test]
    fn test_truncate_all_then_undo() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.truncate(0);
        buf.undo();
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_truncate_end_then_undo() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let end = buf.num_chars();
        buf.truncate(end);
        buf.undo();
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_truncate_part_then_undo() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.truncate(3);
        buf.undo();
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_undo_group() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.remove(0, 1);
        buf.remove(0, 1);
        buf.end_undo_group();
        assert_eq!(buf.undo(), true);
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_redo_group() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.remove(0, 1);
        buf.remove(0, 1);
        buf.end_undo_group();
        assert_eq!(buf.undo(), true);
        assert_eq!(buf.redo(), true);
        assert_eq!(String::from(buf), "defg");
    }

    #[test]
    fn test_nested_undo_group() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.end_undo_group();
        buf.remove(0, 1);
        buf.end_undo_group();
        assert_eq!(buf.undo(), true);
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_nested_redo_group() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.end_undo_group();
        buf.remove(0, 1);
        buf.end_undo_group();
        assert_eq!(buf.undo(), true);
        assert_eq!(buf.redo(), true);
        assert_eq!(String::from(buf), "defg");
    }

    #[test]
    fn test_starts_with() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let mut buf2 = Buffer::new();
        buf2.insert(0, &['a', 'b', 'c']);
        assert_eq!(buf.starts_with(&buf2), true);
    }

    #[test]
    fn test_does_not_start_with() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c']);
        let mut buf2 = Buffer::new();
        buf2.insert(0, &['a', 'b', 'c']);
        assert_eq!(buf.starts_with(&buf2), false);
    }

    #[test]
    fn test_is_not_match2() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let mut buf2 = Buffer::new();
        buf2.insert(0, &['x', 'y', 'z']);
        assert_eq!(buf.starts_with(&buf2), false);
    }

    #[test]
    fn test_print_rest() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let mut buf2 = Buffer::new();
        buf2.insert(0, &['a', 'b', 'c']);
        let mut out: Vec<u8> = vec![];
        buf.print_rest(&mut out, buf2.data.len()).unwrap();
        assert_eq!(out.len(), 4);
    }
}
<commit_msg>implement Buffer.as_bytes()<commit_after>use unicode_width::UnicodeWidthStr;
use std::io::{self, Write};
use std::iter::FromIterator;
use std::fmt::{self, Write as FmtWrite};

#[derive(Debug,Clone)]
pub enum Action {
    Insert { start: usize, text: Vec<char> },
    Remove { start: usize, text: Vec<char> },
    StartGroup,
    EndGroup,
}

impl Action {
    pub fn do_on(&self, buf: &mut Buffer) {
        match *self {
            Action::Insert { start, ref text } => buf.insert_raw(start, &text[..]),
            Action::Remove { start, ref text } => {
                buf.remove_raw(start, start + text.len());
            }
            Action::StartGroup | Action::EndGroup => {}
        }
    }

    pub fn undo(&self, buf: &mut Buffer) {
        match *self {
            Action::Insert { start, ref text } => {
                buf.remove_raw(start, start + text.len());
            }
            Action::Remove { start, ref text } => buf.insert_raw(start, &text[..]),
            Action::StartGroup | Action::EndGroup => {}
        }
    }
}

#[derive(Debug, Clone)]
pub struct Buffer {
    data: Vec<char>,
    actions: Vec<Action>,
    undone_actions: Vec<Action>,
}

impl From<Buffer> for String {
    fn from(buf: Buffer) -> Self {
        String::from_iter(buf.data)
    }
}

impl From<String> for Buffer {
    fn from(s: String) -> Self {
        Buffer::from_iter(s.chars())
    }
}

impl<'a> From<&'a str> for Buffer {
    fn from(s: &'a str) -> Self {
        Buffer::from_iter(s.chars())
    }
}

impl fmt::Display for Buffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for &c in &self.data {
            try!(f.write_char(c));
        }
        Ok(())
    }
}

impl FromIterator<char> for Buffer {
    fn from_iter<T: IntoIterator<Item = char>>(t: T) -> Self {
        Buffer {
            data: t.into_iter().collect(),
            actions: Vec::new(),
            undone_actions: Vec::new(),
        }
    }
}

impl Buffer {
    pub fn new() -> Self {
        Buffer {
            data: Vec::new(),
            actions: Vec::new(),
            undone_actions: Vec::new(),
        }
    }

    pub fn clear_actions(&mut self) {
        self.actions.clear();
        self.undone_actions.clear();
    }

    pub fn start_undo_group(&mut self) {
        self.actions.push(Action::StartGroup);
    }

    pub fn end_undo_group(&mut self) {
        self.actions.push(Action::EndGroup);
    }

    pub fn undo(&mut self) -> bool {
        use Action::*;

        let did = !self.actions.is_empty();
        let mut group_nest = 0;
        let mut group_count = 0;
        while let Some(act) = self.actions.pop() {
            act.undo(self);
            self.undone_actions.push(act.clone());
            match act {
                EndGroup => {
                    group_nest += 1;
                    group_count = 0;
                }
                StartGroup => group_nest -= 1,
                // count the actions in this group so we can ignore empty groups below
                _ => group_count += 1,
            }

            // if we aren't in a group, and the last group wasn't empty
            if group_nest == 0 && group_count > 0 {
                break;
            }
        }
        did
    }

    pub fn redo(&mut self) -> bool {
        use Action::*;

        let did = !self.undone_actions.is_empty();
        let mut group_nest = 0;
        let mut group_count = 0;
        while let Some(act) = self.undone_actions.pop() {
            act.do_on(self);
            self.actions.push(act.clone());
            match act {
                StartGroup => {
                    group_nest += 1;
                    group_count = 0;
                }
                EndGroup => group_nest -= 1,
                // count the actions in this group so we can ignore empty groups below
                _ => group_count += 1,
            }

            // if we aren't in a group, and the last group wasn't empty
            if group_nest == 0 && group_count > 0 {
                break;
            }
        }
        did
    }

    pub fn revert(&mut self) -> bool {
        if self.actions.len() == 0 {
            return false;
        }

        while self.undo() {}
        true
    }

    fn push_action(&mut self, act: Action) {
        self.actions.push(act);
        self.undone_actions.clear();
    }

    pub fn num_chars(&self) -> usize {
        self.data.len()
    }

    pub fn num_bytes(&self) -> usize {
        let s: String = self.clone().into();
        s.len()
    }

    pub fn width(&self) -> usize {
        self.range_width(0, self.num_chars())
    }

    pub fn char_before(&self, cursor: usize) -> Option<char> {
        if cursor == 0 {
            None
        } else {
            self.data.get(cursor - 1).cloned()
        }
    }

    pub fn char_after(&self, cursor: usize) -> Option<char> {
        self.data.get(cursor).cloned()
    }

    /// Returns the number of characters removed.
    pub fn remove(&mut self, start: usize, end: usize) -> usize {
        let s = self.remove_raw(start, end);
        let num_removed = s.len();
        let act = Action::Remove {
            start: start,
            text: s,
        };
        self.push_action(act);
        num_removed
    }

    pub fn insert(&mut self, start: usize, text: &[char]) {
        let act = Action::Insert {
            start: start,
            text: text.into(),
        };
        act.do_on(self);
        self.push_action(act);
    }

    pub fn insert_from_buffer(&mut self, other: &Buffer) {
        let start = self.data.len();
        self.insert(start, &other.data[start..])
    }

    pub fn range(&self, start: usize, end: usize) -> String {
        self.data[start..end].iter().cloned().collect()
    }

    pub fn range_chars(&self, start: usize, end: usize) -> Vec<char> {
        self.data[start..end].iter().cloned().collect()
    }

    pub fn range_width(&self, start: usize, end: usize) -> usize {
        self.range(start, end)[..].width()
    }

    pub fn chars(&self) -> ::std::slice::Iter<char> {
        self.data.iter()
    }

    pub fn truncate(&mut self, num: usize) {
        let end = self.data.len();
        self.remove(num, end);
    }

    pub fn print<W>(&self, out: &mut W) -> io::Result<()>
        where W: Write
    {
        let string: String = self.data.iter().cloned().collect();
        try!(out.write(string.as_bytes()));

        Ok(())
    }

    pub fn as_bytes(&self) -> Vec<u8> {
        // NOTE: not particularly efficient. Could make a proper byte iterator with minimal
        // allocations if performance becomes an issue.
        self.to_string().into_bytes()
    }

    /// Takes other buffer, measures its length and prints this buffer from the point where
    /// the other stopped.
    /// Used to implement autosuggestions.
    pub fn print_rest<W>(&self, out: &mut W, after: usize) -> io::Result<usize>
        where W: Write
    {
        let string: String = self.data.iter().skip(after).cloned().collect();
        out.write(string.as_bytes())?;

        Ok(string.len())
    }

    fn remove_raw(&mut self, start: usize, end: usize) -> Vec<char> {
        self.data.drain(start..end).collect()
    }

    fn insert_raw(&mut self, start: usize, text: &[char]) {
        for (i, &c) in text.iter().enumerate() {
            self.data.insert(start + i, c)
        }
    }

    /// Check if the other buffer starts with the same content as this one.
    /// Used to implement autosuggestions.
    pub fn starts_with(&self, other: &Buffer) -> bool {
        let other_len = other.data.len();
        let self_len = self.data.len();
        if other.data.len() != 0 && self_len != other_len {
            let match_let = self.data
                .iter()
                .zip(&other.data)
                .take_while(|&(s, o)| *s == *o)
                .collect::<Vec<_>>()
                .len();
            match_let == other_len
        } else {
            false
        }
    }

    /// Return true if the buffer is empty.
    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_insert() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_truncate_empty() {
        let mut buf = Buffer::new();
        buf.truncate(0);
        assert_eq!(String::from(buf), "");
    }

    #[test]
    fn test_truncate_all() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.truncate(0);
        assert_eq!(String::from(buf), "");
    }

    #[test]
    fn test_truncate_end() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let end = buf.num_chars();
        buf.truncate(end);
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_truncate_part() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.truncate(3);
        assert_eq!(String::from(buf), "abc");
    }

    #[test]
    fn test_truncate_empty_undo() {
        let mut buf = Buffer::new();
        buf.truncate(0);
        buf.undo();
        assert_eq!(String::from(buf), "");
    }

    #[test]
    fn test_truncate_all_then_undo() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.truncate(0);
        buf.undo();
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_truncate_end_then_undo() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let end = buf.num_chars();
        buf.truncate(end);
        buf.undo();
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_truncate_part_then_undo() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.truncate(3);
        buf.undo();
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_undo_group() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.remove(0, 1);
        buf.remove(0, 1);
        buf.end_undo_group();
        assert_eq!(buf.undo(), true);
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_redo_group() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.remove(0, 1);
        buf.remove(0, 1);
        buf.end_undo_group();
        assert_eq!(buf.undo(), true);
        assert_eq!(buf.redo(), true);
        assert_eq!(String::from(buf), "defg");
    }

    #[test]
    fn test_nested_undo_group() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.end_undo_group();
        buf.remove(0, 1);
        buf.end_undo_group();
        assert_eq!(buf.undo(), true);
        assert_eq!(String::from(buf), "abcdefg");
    }

    #[test]
    fn test_nested_redo_group() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.start_undo_group();
        buf.remove(0, 1);
        buf.end_undo_group();
        buf.remove(0, 1);
        buf.end_undo_group();
        assert_eq!(buf.undo(), true);
        assert_eq!(buf.redo(), true);
        assert_eq!(String::from(buf), "defg");
    }

    #[test]
    fn test_starts_with() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let mut buf2 = Buffer::new();
        buf2.insert(0, &['a', 'b', 'c']);
        assert_eq!(buf.starts_with(&buf2), true);
    }

    #[test]
    fn test_does_not_start_with() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c']);
        let mut buf2 = Buffer::new();
        buf2.insert(0, &['a', 'b', 'c']);
        assert_eq!(buf.starts_with(&buf2), false);
    }

    #[test]
    fn test_is_not_match2() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let mut buf2 = Buffer::new();
        buf2.insert(0, &['x', 'y', 'z']);
        assert_eq!(buf.starts_with(&buf2), false);
    }

    #[test]
    fn test_print_rest() {
        let mut buf = Buffer::new();
        buf.insert(0, &['a', 'b', 'c', 'd', 'e', 'f', 'g']);
        let mut buf2 = Buffer::new();
        buf2.insert(0, &['a', 'b', 'c']);
        let mut out: Vec<u8> = vec![];
        buf.print_rest(&mut out, buf2.data.len()).unwrap();
        assert_eq!(out.len(), 4);
    }
}
<|endoftext|>


<commit_before>use hb;
use std;

use font::Font;
use common::{HarfbuzzObject, Tag};

pub type GlyphPosition = hb::hb_glyph_position_t;
pub type GlyphInfo = hb::hb_glyph_info_t;
pub type Feature = hb::hb_feature_t;

fn language_to_string(tag: hb::hb_language_t) -> &'static str {
    let lang_string_ptr = unsafe { hb::hb_language_to_string(tag) };
    let cstring = unsafe { std::ffi::CStr::from_ptr(lang_string_ptr) };
    cstring.to_str().expect("harfbuzz error: language string is not valid utf-8!")
}

struct BufferRaw {
    hb_buffer: *mut hb::hb_buffer_t,
}
impl BufferRaw {
    fn new() -> BufferRaw {
        let buffer = unsafe { hb::hb_buffer_create() };

        BufferRaw { hb_buffer: buffer }
    }

    fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.hb_buffer
    }

    fn len(&self) -> usize {
        unsafe { hb::hb_buffer_get_length(self.hb_buffer) as usize }
    }

    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    fn add_str(&mut self, string: &str) {
        let utf8_ptr = string.as_ptr() as *const i8;
        unsafe {
            hb::hb_buffer_add_utf8(self.hb_buffer,
                                   utf8_ptr,
                                   string.len() as i32,
                                   0,
                                   string.len() as i32);
        }
    }

    fn set_direction(&mut self, direction: hb::hb_direction_t) {
        unsafe { hb::hb_buffer_set_direction(self.hb_buffer, direction) };
    }

    /// Returns the `Buffer`'s text direction.
    fn get_direction(&self) -> hb::hb_direction_t {
        unsafe { hb::hb_buffer_get_direction(self.hb_buffer) }
    }

    fn set_language(&mut self, lang: hb::hb_language_t) {
        unsafe { hb::hb_buffer_set_language(self.hb_buffer, lang) }
    }

    fn get_language(&self) -> &'static str {
        let lang = unsafe { hb::hb_buffer_get_language(self.hb_buffer) };
        language_to_string(lang)
    }

    fn set_script(&mut self, script: hb::hb_script_t) {
        unsafe { hb::hb_buffer_set_script(self.hb_buffer, script) }
    }

    fn get_script(&self) -> hb::hb_script_t {
        unsafe { hb::hb_buffer_get_script(self.hb_buffer) }
    }

    fn guess_segment_properties(&mut self) {
        unsafe { hb::hb_buffer_guess_segment_properties(self.hb_buffer) };
    }

    fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        unsafe {
            let mut segment_props: hb::hb_segment_properties_t = std::mem::uninitialized();
            hb::hb_buffer_get_segment_properties(self.hb_buffer, &mut segment_props as *mut _);
            segment_props
        }
    }

    fn shape(&mut self, font: &Font, features: &[Feature]) {
        unsafe {
            hb::hb_shape(font.as_raw(),
                         self.hb_buffer,
                         features.as_ptr(),
                         features.len() as u32)
        };
    }

    fn clear_contents(&mut self) {
        unsafe { hb::hb_buffer_clear_contents(self.hb_buffer) };
    }

    fn get_glyph_positions(&self) -> &mut [GlyphPosition] {
        unsafe {
            let mut length: u32 = 0;
            let glyph_pos = hb::hb_buffer_get_glyph_positions(self.hb_buffer,
                                                              &mut length as *mut u32);
            std::slice::from_raw_parts_mut(glyph_pos, length as usize)
        }
    }

    fn get_glyph_infos(&self) -> &mut [GlyphInfo] {
        unsafe {
            let mut length: u32 = 0;
            let glyph_infos = hb::hb_buffer_get_glyph_infos(self.hb_buffer,
                                                            &mut length as *mut u32);
            std::slice::from_raw_parts_mut(glyph_infos, length as usize)
        }
    }

    /// Reverse the `Buffer`'s contents.
    fn reverse(&mut self) {
        unsafe { hb::hb_buffer_reverse(self.hb_buffer) };
    }

    /// Reverse the `Buffer`'s contents in the range from `start` to `end`.
    fn reverse_range(&mut self, start: usize, end: usize) {
        assert!(start <= self.len(), end <= self.len());
        unsafe {
            hb::hb_buffer_reverse_range(self.hb_buffer, start as u32, end as u32)
        }
    }
}

impl Drop for BufferRaw {
    fn drop(&mut self) {
        unsafe {
            hb::hb_buffer_destroy(self.hb_buffer);
        }
    }
}

/// A `UnicodeBuffer` can be filled with unicode text and corresponding
pub struct UnicodeBuffer(BufferRaw);
#[allow(dead_code)]
impl UnicodeBuffer {
    /// Creates a new empty `Buffer`.
    pub fn new() -> UnicodeBuffer {
        UnicodeBuffer(BufferRaw::new())
    }

    /// Returns a pointer to the underlying raw harfbuzz buffer.
    pub fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.0.as_raw()
    }

    /// Returns the length of the data of the buffer.
    ///
    /// When called before shaping this is the number of unicode codepoints contained in the
    /// buffer. When called after shaping it returns the number of glyphs stored.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Adds the string slice to the `Buffer`'s array of codepoints.
    pub fn add_str(mut self, string: &str) -> UnicodeBuffer {
        self.0.add_str(string);
        self
    }

    fn get_string(&self) -> String {
        let mut result = String::with_capacity(self.len());
        for info in self.0.get_glyph_infos() {
            let chr = std::char::from_u32(info.codepoint).unwrap();
            result.push(chr)
        }
        result
    }

    /// Sets the text direction of the `Buffer`'s contents.
    pub fn set_direction(mut self, direction: hb::hb_direction_t) -> UnicodeBuffer {
        self.0.set_direction(direction);
        self
    }

    /// Returns the `Buffer`'s text direction.
    pub fn get_direction(&self) -> hb::hb_direction_t {
        self.0.get_direction()
    }

    pub fn set_script(mut self, script: Tag) -> UnicodeBuffer {
        self.0.set_script(script.0);
        self
    }

    pub fn get_script(&self) -> Tag {
        Tag(self.0.get_script())
    }

    pub fn get_language(&self) -> &'static str {
        self.0.get_language()
    }

    pub fn guess_segment_properties(mut self) -> UnicodeBuffer {
        self.0.guess_segment_properties();
        self
    }

    pub fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        self.0.get_segment_properties()
    }

    pub fn shape(mut self, font: &Font, features: &[Feature]) -> GlyphBuffer {
        self = self.guess_segment_properties();
        self.0.shape(font, features);
        GlyphBuffer(self.0)
    }

    pub fn clear_contents(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        self
    }
}

impl std::fmt::Debug for UnicodeBuffer {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.debug_struct("Buffer")
            .field("content", &self.get_string())
            .field("direction", &self.get_direction())
            .field("language", &self.get_language().to_owned())
            .finish()
    }
}

impl std::default::Default for UnicodeBuffer {
    fn default() -> UnicodeBuffer {
        UnicodeBuffer::new()
    }
}

/// A `GlyphBuffer` is obtained through the `shape` function of a `UnicodeBuffer`. It contains
/// the resulting output information of the shaping process.
pub struct GlyphBuffer(BufferRaw);

impl GlyphBuffer {
    /// Returns the length of the data of the buffer.
    ///
    /// When called before shaping this is the number of unicode codepoints contained in the
    /// buffer. When called after shaping it returns the number of glyphs stored.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn get_glyph_positions(&self) -> &mut [GlyphPosition] {
        self.0.get_glyph_positions()
    }

    pub fn get_glyph_infos(&self) -> &mut [GlyphInfo] {
        self.0.get_glyph_infos()
    }

    /// Reverse the `Buffer`'s contents.
    pub fn reverse(&mut self) {
        self.0.reverse()
    }

    /// Reverse the `Buffer`'s contents in the range from `start` to `end`.
    pub fn reverse_range(&mut self, start: usize, end: usize) {
        self.0.reverse_range(start, end)
    }

    /// Clears the contents of the glyph buffer and returns an empty `UnicodeBuffer` reusing the
    /// existing allocation.
    pub fn clear(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        UnicodeBuffer(self.0)
    }
}

pub enum Buffer {
    Unicode(UnicodeBuffer),
    Glyph(GlyphBuffer),
}
<commit_msg>Add Debug implementation for Buffer.<commit_after>use hb;
use std;

use font::Font;
use common::{HarfbuzzObject, Tag};

pub type GlyphPosition = hb::hb_glyph_position_t;
pub type GlyphInfo = hb::hb_glyph_info_t;
pub type Feature = hb::hb_feature_t;

fn language_to_string(tag: hb::hb_language_t) -> &'static str {
    let lang_string_ptr = unsafe { hb::hb_language_to_string(tag) };
    let cstring = unsafe { std::ffi::CStr::from_ptr(lang_string_ptr) };
    cstring.to_str().expect("harfbuzz error: language string is not valid utf-8!")
}

struct BufferRaw {
    hb_buffer: *mut hb::hb_buffer_t,
}
impl BufferRaw {
    fn new() -> BufferRaw {
        let buffer = unsafe { hb::hb_buffer_create() };

        BufferRaw { hb_buffer: buffer }
    }

    fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.hb_buffer
    }

    fn len(&self) -> usize {
        unsafe { hb::hb_buffer_get_length(self.hb_buffer) as usize }
    }

    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    fn add_str(&mut self, string: &str) {
        let utf8_ptr = string.as_ptr() as *const i8;
        unsafe {
            hb::hb_buffer_add_utf8(self.hb_buffer,
                                   utf8_ptr,
                                   string.len() as i32,
                                   0,
                                   string.len() as i32);
        }
    }

    fn set_direction(&mut self, direction: hb::hb_direction_t) {
        unsafe { hb::hb_buffer_set_direction(self.hb_buffer, direction) };
    }

    /// Returns the `Buffer`'s text direction.
    fn get_direction(&self) -> hb::hb_direction_t {
        unsafe { hb::hb_buffer_get_direction(self.hb_buffer) }
    }

    fn set_language(&mut self, lang: hb::hb_language_t) {
        unsafe { hb::hb_buffer_set_language(self.hb_buffer, lang) }
    }

    fn get_language(&self) -> &'static str {
        let lang = unsafe { hb::hb_buffer_get_language(self.hb_buffer) };
        language_to_string(lang)
    }

    fn set_script(&mut self, script: hb::hb_script_t) {
        unsafe { hb::hb_buffer_set_script(self.hb_buffer, script) }
    }

    fn get_script(&self) -> hb::hb_script_t {
        unsafe { hb::hb_buffer_get_script(self.hb_buffer) }
    }

    fn guess_segment_properties(&mut self) {
        unsafe { hb::hb_buffer_guess_segment_properties(self.hb_buffer) };
    }

    fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        unsafe {
            let mut segment_props: hb::hb_segment_properties_t = std::mem::uninitialized();
            hb::hb_buffer_get_segment_properties(self.hb_buffer, &mut segment_props as *mut _);
            segment_props
        }
    }

    fn shape(&mut self, font: &Font, features: &[Feature]) {
        unsafe {
            hb::hb_shape(font.as_raw(),
                         self.hb_buffer,
                         features.as_ptr(),
                         features.len() as u32)
        };
    }

    fn clear_contents(&mut self) {
        unsafe { hb::hb_buffer_clear_contents(self.hb_buffer) };
    }

    fn get_glyph_positions(&self) -> &mut [GlyphPosition] {
        unsafe {
            let mut length: u32 = 0;
            let glyph_pos = hb::hb_buffer_get_glyph_positions(self.hb_buffer,
                                                              &mut length as *mut u32);
            std::slice::from_raw_parts_mut(glyph_pos, length as usize)
        }
    }

    fn get_glyph_infos(&self) -> &mut [GlyphInfo] {
        unsafe {
            let mut length: u32 = 0;
            let glyph_infos = hb::hb_buffer_get_glyph_infos(self.hb_buffer,
                                                            &mut length as *mut u32);
            std::slice::from_raw_parts_mut(glyph_infos, length as usize)
        }
    }

    /// Reverse the `Buffer`'s contents.
    fn reverse(&mut self) {
        unsafe { hb::hb_buffer_reverse(self.hb_buffer) };
    }

    /// Reverse the `Buffer`'s contents in the range from `start` to `end`.
    fn reverse_range(&mut self, start: usize, end: usize) {
        assert!(start <= self.len(), end <= self.len());
        unsafe {
            hb::hb_buffer_reverse_range(self.hb_buffer, start as u32, end as u32)
        }
    }
}

impl Drop for BufferRaw {
    fn drop(&mut self) {
        unsafe {
            hb::hb_buffer_destroy(self.hb_buffer);
        }
    }
}

/// A `UnicodeBuffer` can be filled with unicode text and corresponding
pub struct UnicodeBuffer(BufferRaw);
#[allow(dead_code)]
impl UnicodeBuffer {
    /// Creates a new empty `Buffer`.
    pub fn new() -> UnicodeBuffer {
        UnicodeBuffer(BufferRaw::new())
    }

    /// Returns a pointer to the underlying raw harfbuzz buffer.
    pub fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.0.as_raw()
    }

    /// Returns the length of the data of the buffer.
    ///
    /// When called before shaping this is the number of unicode codepoints contained in the
    /// buffer. When called after shaping it returns the number of glyphs stored.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Adds the string slice to the `Buffer`'s array of codepoints.
    pub fn add_str(mut self, string: &str) -> UnicodeBuffer {
        self.0.add_str(string);
        self
    }

    fn get_string(&self) -> String {
        let mut result = String::with_capacity(self.len());
        for info in self.0.get_glyph_infos() {
            let chr = std::char::from_u32(info.codepoint).unwrap();
            result.push(chr)
        }
        result
    }

    /// Sets the text direction of the `Buffer`'s contents.
    pub fn set_direction(mut self, direction: hb::hb_direction_t) -> UnicodeBuffer {
        self.0.set_direction(direction);
        self
    }

    /// Returns the `Buffer`'s text direction.
    pub fn get_direction(&self) -> hb::hb_direction_t {
        self.0.get_direction()
    }

    pub fn set_script(mut self, script: Tag) -> UnicodeBuffer {
        self.0.set_script(script.0);
        self
    }

    pub fn get_script(&self) -> Tag {
        Tag(self.0.get_script())
    }

    pub fn get_language(&self) -> &'static str {
        self.0.get_language()
    }

    pub fn guess_segment_properties(mut self) -> UnicodeBuffer {
        self.0.guess_segment_properties();
        self
    }

    pub fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        self.0.get_segment_properties()
    }

    pub fn shape(mut self, font: &Font, features: &[Feature]) -> GlyphBuffer {
        self = self.guess_segment_properties();
        self.0.shape(font, features);
        GlyphBuffer(self.0)
    }

    pub fn clear_contents(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        self
    }
}

impl std::fmt::Debug for UnicodeBuffer {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.debug_struct("Buffer")
            .field("content", &self.get_string())
            .field("direction", &self.get_direction())
            .field("language", &self.get_language().to_owned())
            .finish()
    }
}

impl std::default::Default for UnicodeBuffer {
    fn default() -> UnicodeBuffer {
        UnicodeBuffer::new()
    }
}

/// A `GlyphBuffer` is obtained through the `shape` function of a `UnicodeBuffer`. It contains
/// the resulting output information of the shaping process.
pub struct GlyphBuffer(BufferRaw);

impl GlyphBuffer {
    /// Returns the length of the data of the buffer.
    ///
    /// When called before shaping this is the number of unicode codepoints contained in the
    /// buffer. When called after shaping it returns the number of glyphs stored.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn get_glyph_positions(&self) -> &mut [GlyphPosition] {
        self.0.get_glyph_positions()
    }

    pub fn get_glyph_infos(&self) -> &mut [GlyphInfo] {
        self.0.get_glyph_infos()
    }

    /// Reverse the `Buffer`'s contents.
    pub fn reverse(&mut self) {
        self.0.reverse()
    }

    /// Reverse the `Buffer`'s contents in the range from `start` to `end`.
    pub fn reverse_range(&mut self, start: usize, end: usize) {
        self.0.reverse_range(start, end)
    }

    /// Clears the contents of the glyph buffer and returns an empty `UnicodeBuffer` reusing the
    /// existing allocation.
    pub fn clear(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        UnicodeBuffer(self.0)
    }
}

impl std::fmt::Debug for GlyphBuffer {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.debug_struct("Buffer")
            .field("glyph positions", &self.get_glyph_positions())
            .field("glyph infos", &self.get_glyph_infos())
            .finish()
    }
}

#[derive(Debug)]
pub enum Buffer {
    UnicodeBuffer(UnicodeBuffer),
    GlyphBuffer(GlyphBuffer),
}
<|endoftext|>


<commit_before>use crate::{
    diagnostics::{
        self,
        db::{FileId, SourceFileCache},
        Diagnostic, Diagnostics, Outcome,
    },
    ir, libclang,
};
use cc_use_common::{CcPath, CcUse};
use proc_macro2::Span;
use std::fs::File;
use std::{
    collections::BTreeMap,
    io::{self, Read},
    path::Path,
    sync::Arc,
};
use syn::{self, spanned::Spanned};

const MACRO_NAME: &'static str = "cc_use";

/// Provides access to Rust source files.
#[salsa::query_group(RsSourceStorage)]
pub trait RsSource: SourceFileCache {
    /// The source root of the Rust crate we're generating bindings for, if any.
    #[salsa::input]
    fn rs_source_root(&self) -> Option<FileId>;
}

/// Generates IR for `cc_use!` imports in Rust source.
#[salsa::query_group(RsImportIrStorage)]
pub trait RsImportIr: RsSource {
    #[doc(hidden)]
    fn headers_with_imports(&self) -> Arc<Outcome<Vec<HeaderInfo>>>;

    /// The range of `ModuleId`s used.
    ///
    /// WARNING: This is only useful when using Rust source. `AstMethods` has the method that
    /// always works.
    fn module_ids(&self) -> Outcome<Arc<[ir::bindings::ModuleId]>>;

    /// The set of headers imported in the Rust crate.
    fn headers(&self) -> Outcome<Arc<[ir::bindings::Header]>>;

    /// The set of (top-level) imports in the Rust crate.
    fn imports(&self) -> Outcome<Arc<[ir::bindings::Import]>>;

    /// The set of (top-level) imports in the Rust crate for a given `ModuleId`.
    fn imports_for(&self, mdl: ir::bindings::ModuleId) -> Outcome<Arc<[ir::bindings::Import]>>;
}

/// Parses the C++ header file for the given `ModuleId`.
// This should probably be a query, but the libclang types don't easily go in salsa.
pub(crate) fn cc_module_from_rs(
    db: &impl RsImportIr,
    index: &libclang::Index,
    module_id: ir::bindings::ModuleId,
) -> (libclang::ModuleContext, libclang::ParseErrors) {
    let rs_source_path = db.rs_source_root().unwrap().name(db);
    let hdrs = db.headers_with_imports();
    // skip_errs okay since the user must go through an outcome with these errors to get module_id.
    let hdr = &hdrs.to_ref().skip_errs()[module_id.as_usize()];
    let parse = load_cc_module(
        db,
        index,
        Path::new(&rs_source_path),
        &hdr.header,
        hdr.module_id,
    );
    parse
}

#[doc(hidden)]
#[derive(Debug, Eq, PartialEq)]
pub struct HeaderInfo {
    module_id: ir::bindings::ModuleId,
    header: ir::bindings::Header,
    imports: Vec<ir::bindings::Import>,
}

// Parse Rust file and return IR imports grouped by header.
fn headers_with_imports(db: &impl RsImportIr) -> Arc<Outcome<Vec<HeaderInfo>>> {
    let file_id = db.rs_source_root().unwrap();
    let headers = parse_rs_file(db, &file_id.contents(db), file_id).then(|macros| {
        let mut header_map = BTreeMap::new();
        for mac in &macros {
            header_map
                .entry((&mac.header.path, mac.header.is_system))
                .or_insert((mac.header.span, Vec::new()))
                .1
                .push(&mac.cc_paths);
        }
        let mut errs = Diagnostics::new();
        let headers = header_map
            .iter()
            .enumerate()
            .map(|(id, (hdr, macros))| {
                let module_id = ir::bindings::ModuleId::new(id as _);
                let header = ir::bindings::Header {
                    path: hdr.0.clone(),
                    is_system: hdr.1,
                    span: Some(span(db, file_id, macros.0)),
                };
                let imports = macros
                    .1
                    .iter()
                    .copied()
                    .flatten()
                    .flat_map(|cc_path| {
                        let (path, err) = convert_path(db, file_id, cc_path).split();
                        errs.append(err);
                        path.map(|ir_path| ir::bindings::Import {
                            mdl: module_id,
                            path: ir_path,
                            span: span(db, file_id, cc_path.span()),
                        })
                    })
                    .collect();
                HeaderInfo {
                    module_id,
                    header,
                    imports,
                }
            })
            .collect();
        Outcome::from_parts(headers, errs)
    });
    Arc::new(headers)
}

fn module_ids(db: &impl RsImportIr) -> Outcome<Arc<[ir::bindings::ModuleId]>> {
    db.headers_with_imports().to_ref().map(|hdrs| {
        (0..(hdrs.len() as _))
            .map(ir::bindings::ModuleId::new)
            .collect()
    })
}

fn headers(db: &impl RsImportIr) -> Outcome<Arc<[ir::bindings::Header]>> {
    db.headers_with_imports()
        .to_ref()
        .map(|data| data.iter().map(|hdr| hdr.header.clone()).collect())
}

fn imports(db: &impl RsImportIr) -> Outcome<Arc<[ir::bindings::Import]>> {
    if db.rs_source_root().is_none() {
        return Outcome::from_ok(Arc::new([]));
    }
    db.headers_with_imports().to_ref().map(|data| {
        data.iter()
            .flat_map(|hdr| hdr.imports.iter().cloned())
            .collect()
    })
}

fn imports_for(
    db: &impl RsImportIr,
    mdl: ir::bindings::ModuleId,
) -> Outcome<Arc<[ir::bindings::Import]>> {
    if db.rs_source_root().is_none() {
        return Outcome::from_ok(Arc::new([]));
    }
    db.headers_with_imports().to_ref().map(|data| {
        let mut headers = data.iter().filter(|hdr| hdr.module_id == mdl);
        let header = headers.next().unwrap();
        debug_assert!(headers.next().is_none());
        header.imports.clone().into()
    })
}

fn parse_rs_file(
    db: &impl SourceFileCache,
    contents: &str,
    file_id: FileId,
) -> Outcome<Vec<CcUse>> {
    // TODO: we can improve performance by not parsing everything in the file.
    let ast = match syn::parse_file(&contents) {
        Ok(ast) => ast,
        Err(err) => return Outcome::from_parts(vec![], error(db, file_id, err)),
    };

    // TODO: actually resolve names enough to know if this is our macro or not, and recognize
    // alternate names.
    let mut macros = vec![];
    let mut errs = Diagnostics::new();
    for item in ast.items {
        match item {
            syn::Item::Macro(m)
                if MACRO_NAME == m.mac.path.segments.last().unwrap().ident.to_string() =>
            {
                let cc_use: CcUse = match m.mac.parse_body() {
                    Ok(cc_use) => cc_use,
                    Err(err) => {
                        errs.append(error(db, file_id, err));
                        continue;
                    }
                };
                macros.push(cc_use);
            }
            _ => (),
        }
    }
    Outcome::from_parts(macros, errs)
}

fn load_cc_module(
    db: &impl SourceFileCache,
    index: &libclang::Index,
    rs_src_path: &Path,
    header: &ir::bindings::Header,
    module_id: ir::bindings::ModuleId,
) -> (libclang::ModuleContext, libclang::ParseErrors) {
    let (ctx, errs) = libclang::parse_with(db, index, module_id, |index| {
        let line = header.span.as_ref().map(|_| 1).unwrap(); // TODO
        let src = if header.is_system {
            // TODO: This line directive doesn't have the intended effect. We can either figure
            // out a way to use the "presumed location" libclang gives us, or find a way to present
            // a better error ourselves.
            format!("#line {}\n#include <{}>", line, header.path)
        } else {
            format!("#line {}\n#include \"{}\"", line, header.path)
        };
        // Use the rust source as the path so it shows up in "not found" errors
        let mut parser = libclang::configure(index.parser(rs_src_path));
        let unsaved = clang::Unsaved::new(rs_src_path, src);
        // unwrap is okay because we know the "file" exists.
        parser.unsaved(&[unsaved]).parse().unwrap()
    });
    (ctx, errs)
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct SourceFile(Arc<SourceFileInner>);
impl SourceFile {
    pub(crate) fn intern_from_path(
        db: &impl SourceFileCache,
        input_path: &Path,
    ) -> io::Result<FileId> {
        Ok(db.intern_source_file(crate::SourceFileKind::Rs(Self::from_path(input_path)?)))
    }

    fn from_path(path: &Path) -> io::Result<Self> {
        let mut contents = String::new();
        File::open(path)?.read_to_string(&mut contents)?;
        Ok(SourceFile(Arc::new(SourceFileInner {
            name: path.to_string_lossy().to_string(),
            contents,
        })))
    }

    pub(crate) fn get_name_and_contents(&self) -> (String, String) {
        (self.0.name.clone(), self.0.contents.clone())
    }
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct SourceFileInner {
    name: String,
    contents: String,
}

fn span(db: &impl SourceFileCache, file_id: FileId, span: Span) -> diagnostics::Span {
    // In proc_macro2 the lines are 1-indexed and columns are 0-indexed, whereas in codespan they
    // are both 0-indexed.
    let start =
        diagnostics::Location::new(span.start().line as u32 - 1, span.start().column as u32);
    let end = diagnostics::Location::new(span.end().line as u32 - 1, span.end().column as u32);
    diagnostics::Span::from_location(db, file_id, start, end)
}

fn error(db: &impl SourceFileCache, file_id: FileId, input: syn::Error) -> Diagnostics {
    Diagnostics::build(|errs| {
        for err in input {
            // For now we duplicate the error message as both the header and label text.
            errs.add(Diagnostic::error(
                err.to_string(),
                span(db, file_id, err.span()).label(err.to_string()),
            ));
        }
    })
}

fn convert_path(
    db: &impl SourceFileCache,
    file_id: FileId,
    other: &CcPath,
) -> Outcome<Option<ir::bindings::Path>> {
    let invalid_arg = |sp| {
        Outcome::from_err(
            None,
            Diagnostic::error(
                "invalid template argument",
                span(db, file_id, sp).label("only basic types like `u32` are supported"),
            ),
        )
    };
    let mut components = vec![];
    for segment in &other.segments {
        let mut component_args = vec![];
        match &segment.arguments {
            syn::PathArguments::None => (),
            syn::PathArguments::AngleBracketed(args) => {
                for arg in args.args.iter() {
                    match arg {
                        syn::GenericArgument::Type(syn::Type::Path(p)) => {
                            if p.qself.is_some() {
                                return invalid_arg(p.span());
                            }
                            let (converted, err) = convert_path(db, file_id, &p.path).split();
                            if !err.is_empty() {
                                assert!(converted.is_none());
                                return Outcome::from_parts(None, err);
                            }
                            component_args.push(converted.unwrap());
                        }
                        _ => return invalid_arg(arg.span()),
                    }
                }
            }
            syn::PathArguments::Parenthesized(args) => return invalid_arg(args.span()),
        }
        components.push(ir::bindings::PathComponent {
            name: ir::bindings::Ident::from(segment.ident.to_string()),
            args: component_args,
        });
    }
    Outcome::from_ok(Some(ir::bindings::Path::from(components)))
}
<commit_msg>Use codegen macros to write C++ in cc_use<commit_after>use crate::{
    codegen::CodeWriter,
    diagnostics::{
        self,
        db::{FileId, SourceFileCache},
        Diagnostic, Diagnostics, Outcome,
    },
    ir, libclang,
};
use cc_use_common::{CcPath, CcUse};
use gen_macro::{snippet, write_gen, Snippet};
use proc_macro2::Span;
use std::fs::File;
use std::{
    collections::BTreeMap,
    io::{self, Read},
    path::Path,
    sync::Arc,
};
use syn::{self, spanned::Spanned};

const MACRO_NAME: &'static str = "cc_use";

/// Provides access to Rust source files.
#[salsa::query_group(RsSourceStorage)]
pub trait RsSource: SourceFileCache {
    /// The source root of the Rust crate we're generating bindings for, if any.
    #[salsa::input]
    fn rs_source_root(&self) -> Option<FileId>;
}

/// Generates IR for `cc_use!` imports in Rust source.
#[salsa::query_group(RsImportIrStorage)]
pub trait RsImportIr: RsSource {
    #[doc(hidden)]
    fn headers_with_imports(&self) -> Arc<Outcome<Vec<HeaderInfo>>>;

    /// The range of `ModuleId`s used.
    ///
    /// WARNING: This is only useful when using Rust source. `AstMethods` has the method that
    /// always works.
    fn module_ids(&self) -> Outcome<Arc<[ir::bindings::ModuleId]>>;

    /// The set of headers imported in the Rust crate.
    fn headers(&self) -> Outcome<Arc<[ir::bindings::Header]>>;

    /// The set of (top-level) imports in the Rust crate.
    fn imports(&self) -> Outcome<Arc<[ir::bindings::Import]>>;

    /// The set of (top-level) imports in the Rust crate for a given `ModuleId`.
    fn imports_for(&self, mdl: ir::bindings::ModuleId) -> Outcome<Arc<[ir::bindings::Import]>>;
}

/// Parses the C++ header file for the given `ModuleId`.
// This should probably be a query, but the libclang types don't easily go in salsa.
pub(crate) fn cc_module_from_rs(
    db: &impl RsImportIr,
    index: &libclang::Index,
    module_id: ir::bindings::ModuleId,
) -> (libclang::ModuleContext, libclang::ParseErrors) {
    let rs_source_path = db.rs_source_root().unwrap().name(db);
    let hdrs = db.headers_with_imports();
    // skip_errs okay since the user must go through an outcome with these errors to get module_id.
    let hdr = &hdrs.to_ref().skip_errs()[module_id.as_usize()];
    let parse = load_cc_module(
        db,
        index,
        Path::new(&rs_source_path),
        &hdr.header,
        hdr.module_id,
    );
    parse
}

#[doc(hidden)]
#[derive(Debug, Eq, PartialEq)]
pub struct HeaderInfo {
    module_id: ir::bindings::ModuleId,
    header: ir::bindings::Header,
    imports: Vec<ir::bindings::Import>,
}

// Parse Rust file and return IR imports grouped by header.
fn headers_with_imports(db: &impl RsImportIr) -> Arc<Outcome<Vec<HeaderInfo>>> {
    let file_id = db.rs_source_root().unwrap();
    let headers = parse_rs_file(db, &file_id.contents(db), file_id).then(|macros| {
        let mut header_map = BTreeMap::new();
        for mac in &macros {
            header_map
                .entry((&mac.header.path, mac.header.is_system))
                .or_insert((mac.header.span, Vec::new()))
                .1
                .push(&mac.cc_paths);
        }
        let mut errs = Diagnostics::new();
        let headers = header_map
            .iter()
            .enumerate()
            .map(|(id, (hdr, macros))| {
                let module_id = ir::bindings::ModuleId::new(id as _);
                let header = ir::bindings::Header {
                    path: hdr.0.clone(),
                    is_system: hdr.1,
                    span: Some(span(db, file_id, macros.0)),
                };
                let imports = macros
                    .1
                    .iter()
                    .copied()
                    .flatten()
                    .flat_map(|cc_path| {
                        let (path, err) = convert_path(db, file_id, cc_path).split();
                        errs.append(err);
                        path.map(|ir_path| ir::bindings::Import {
                            mdl: module_id,
                            path: ir_path,
                            span: span(db, file_id, cc_path.span()),
                        })
                    })
                    .collect();
                HeaderInfo {
                    module_id,
                    header,
                    imports,
                }
            })
            .collect();
        Outcome::from_parts(headers, errs)
    });
    Arc::new(headers)
}

fn module_ids(db: &impl RsImportIr) -> Outcome<Arc<[ir::bindings::ModuleId]>> {
    db.headers_with_imports().to_ref().map(|hdrs| {
        (0..(hdrs.len() as _))
            .map(ir::bindings::ModuleId::new)
            .collect()
    })
}

fn headers(db: &impl RsImportIr) -> Outcome<Arc<[ir::bindings::Header]>> {
    db.headers_with_imports()
        .to_ref()
        .map(|data| data.iter().map(|hdr| hdr.header.clone()).collect())
}

fn imports(db: &impl RsImportIr) -> Outcome<Arc<[ir::bindings::Import]>> {
    if db.rs_source_root().is_none() {
        return Outcome::from_ok(Arc::new([]));
    }
    db.headers_with_imports().to_ref().map(|data| {
        data.iter()
            .flat_map(|hdr| hdr.imports.iter().cloned())
            .collect()
    })
}

fn imports_for(
    db: &impl RsImportIr,
    mdl: ir::bindings::ModuleId,
) -> Outcome<Arc<[ir::bindings::Import]>> {
    if db.rs_source_root().is_none() {
        return Outcome::from_ok(Arc::new([]));
    }
    db.headers_with_imports().to_ref().map(|data| {
        let mut headers = data.iter().filter(|hdr| hdr.module_id == mdl);
        let header = headers.next().unwrap();
        debug_assert!(headers.next().is_none());
        header.imports.clone().into()
    })
}

fn parse_rs_file(
    db: &impl SourceFileCache,
    contents: &str,
    file_id: FileId,
) -> Outcome<Vec<CcUse>> {
    // TODO: we can improve performance by not parsing everything in the file.
    let ast = match syn::parse_file(&contents) {
        Ok(ast) => ast,
        Err(err) => return Outcome::from_parts(vec![], error(db, file_id, err)),
    };

    // TODO: actually resolve names enough to know if this is our macro or not, and recognize
    // alternate names.
    let mut macros = vec![];
    let mut errs = Diagnostics::new();
    for item in ast.items {
        match item {
            syn::Item::Macro(m)
                if MACRO_NAME == m.mac.path.segments.last().unwrap().ident.to_string() =>
            {
                let cc_use: CcUse = match m.mac.parse_body() {
                    Ok(cc_use) => cc_use,
                    Err(err) => {
                        errs.append(error(db, file_id, err));
                        continue;
                    }
                };
                macros.push(cc_use);
            }
            _ => (),
        }
    }
    Outcome::from_parts(macros, errs)
}

#[rustfmt::skip::macros(write_gen)]
fn load_cc_module(
    db: &impl SourceFileCache,
    index: &libclang::Index,
    rs_src_path: &Path,
    header: &ir::bindings::Header,
    module_id: ir::bindings::ModuleId,
) -> (libclang::ModuleContext, libclang::ParseErrors) {
    let (ctx, errs) = libclang::parse_with(db, index, module_id, |index| {
        let mut code = Vec::new();
        let writer = &mut CodeWriter::new(&mut code);

        let path: Snippet = header.path.clone().into();
        let quoted_path = if header.is_system {
            snippet!(db, "<$path>")
        } else {
            snippet!(db, r#""$path""#)
        };
        // TODO: This line directive doesn't have the intended effect. We can either figure
        // out a way to use the "presumed location" libclang gives us, or find a way to present
        // a better error ourselves.
        let line: Snippet = header.span.as_ref().map(|_| 1).unwrap().to_string().into(); // TODO
        write_gen!(db, writer, "
            #line $line
            #include $quoted_path
        ")
        .unwrap();

        let code = std::str::from_utf8(&code).unwrap();

        // Use the rust source as the path so it shows up in "not found" errors
        let mut parser = libclang::configure(index.parser(rs_src_path));
        let unsaved = clang::Unsaved::new(rs_src_path, code);
        // unwrap is okay because we know the "file" exists.
        parser.unsaved(&[unsaved]).parse().unwrap()
    });
    (ctx, errs)
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct SourceFile(Arc<SourceFileInner>);
impl SourceFile {
    pub(crate) fn intern_from_path(
        db: &impl SourceFileCache,
        input_path: &Path,
    ) -> io::Result<FileId> {
        Ok(db.intern_source_file(crate::SourceFileKind::Rs(Self::from_path(input_path)?)))
    }

    fn from_path(path: &Path) -> io::Result<Self> {
        let mut contents = String::new();
        File::open(path)?.read_to_string(&mut contents)?;
        Ok(SourceFile(Arc::new(SourceFileInner {
            name: path.to_string_lossy().to_string(),
            contents,
        })))
    }

    pub(crate) fn get_name_and_contents(&self) -> (String, String) {
        (self.0.name.clone(), self.0.contents.clone())
    }
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct SourceFileInner {
    name: String,
    contents: String,
}

fn span(db: &impl SourceFileCache, file_id: FileId, span: Span) -> diagnostics::Span {
    // In proc_macro2 the lines are 1-indexed and columns are 0-indexed, whereas in codespan they
    // are both 0-indexed.
    let start =
        diagnostics::Location::new(span.start().line as u32 - 1, span.start().column as u32);
    let end = diagnostics::Location::new(span.end().line as u32 - 1, span.end().column as u32);
    diagnostics::Span::from_location(db, file_id, start, end)
}

fn error(db: &impl SourceFileCache, file_id: FileId, input: syn::Error) -> Diagnostics {
    Diagnostics::build(|errs| {
        for err in input {
            // For now we duplicate the error message as both the header and label text.
            errs.add(Diagnostic::error(
                err.to_string(),
                span(db, file_id, err.span()).label(err.to_string()),
            ));
        }
    })
}

fn convert_path(
    db: &impl SourceFileCache,
    file_id: FileId,
    other: &CcPath,
) -> Outcome<Option<ir::bindings::Path>> {
    let invalid_arg = |sp| {
        Outcome::from_err(
            None,
            Diagnostic::error(
                "invalid template argument",
                span(db, file_id, sp).label("only basic types like `u32` are supported"),
            ),
        )
    };
    let mut components = vec![];
    for segment in &other.segments {
        let mut component_args = vec![];
        match &segment.arguments {
            syn::PathArguments::None => (),
            syn::PathArguments::AngleBracketed(args) => {
                for arg in args.args.iter() {
                    match arg {
                        syn::GenericArgument::Type(syn::Type::Path(p)) => {
                            if p.qself.is_some() {
                                return invalid_arg(p.span());
                            }
                            let (converted, err) = convert_path(db, file_id, &p.path).split();
                            if !err.is_empty() {
                                assert!(converted.is_none());
                                return Outcome::from_parts(None, err);
                            }
                            component_args.push(converted.unwrap());
                        }
                        _ => return invalid_arg(arg.span()),
                    }
                }
            }
            syn::PathArguments::Parenthesized(args) => return invalid_arg(args.span()),
        }
        components.push(ir::bindings::PathComponent {
            name: ir::bindings::Ident::from(segment.ident.to_string()),
            args: component_args,
        });
    }
    Outcome::from_ok(Some(ir::bindings::Path::from(components)))
}
<|endoftext|>