<commit_before>use hir::HirDisplay;
use ra_syntax::{
    ast::{self, AstNode, LetStmt, NameOwner, TypeAscriptionOwner},
    TextRange,
};

use crate::{AssistContext, AssistId, Assists};

// Assist: add_explicit_type
//
// Specify type for a let binding.
//
// ```
// fn main() {
//     let x<|> = 92;
// }
// ```
// ->
// ```
// fn main() {
//     let x: i32 = 92;
// }
// ```
pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let stmt = ctx.find_node_at_offset::<LetStmt>()?;
    let module = ctx.sema.scope(stmt.syntax()).module()?;
    let expr = stmt.initializer()?;
    let pat = stmt.pat()?;
    // Must be a binding
    let pat = match pat {
        ast::Pat::BindPat(bind_pat) => bind_pat,
        _ => return None,
    };
    let pat_range = pat.syntax().text_range();
    // The binding must have a name
    let name = pat.name()?;
    let name_range = name.syntax().text_range();
    let stmt_range = stmt.syntax().text_range();
    let eq_range = stmt.eq_token()?.text_range();
    // Assist should only be applicable if cursor is between 'let' and '='
    let let_range = TextRange::new(stmt_range.start(), eq_range.start());
    let cursor_in_range = let_range.contains_range(ctx.frange.range);
    if !cursor_in_range {
        return None;
    }
    // Assist not applicable if the type has already been specified
    // and it has no placeholders
    let ascribed_ty = stmt.ascribed_type();
    if let Some(ref ty) = ascribed_ty {
        if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
            return None;
        }
    }
    // Infer type
    let ty = ctx.sema.type_of_expr(&expr)?;

    if ty.contains_unknown() || ty.is_closure() {
        return None;
    }

    let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?;
    acc.add(
        AssistId("add_explicit_type"),
        format!("Insert explicit type `{}`", inferred_type),
        pat_range,
        |builder| match ascribed_ty {
            Some(ascribed_ty) => {
                builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
            }
            None => {
                builder.insert(name_range.end(), format!(": {}", inferred_type));
            }
        },
    )
}

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

    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn add_explicit_type_target() {
        check_assist_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a");
    }

    #[test]
    fn add_explicit_type_works_for_simple_expr() {
        check_assist(
            add_explicit_type,
            "fn f() { let a<|> = 1; }",
            "fn f() { let a<|>: i32 = 1; }",
        );
    }

    #[test]
    fn add_explicit_type_works_for_underscore() {
        check_assist(
            add_explicit_type,
            "fn f() { let a<|>: _ = 1; }",
            "fn f() { let a<|>: i32 = 1; }",
        );
    }

    #[test]
    fn add_explicit_type_works_for_nested_underscore() {
        check_assist(
            add_explicit_type,
            r#"
            enum Option<T> {
                Some(T),
                None
            }

            fn f() {
                let a<|>: Option<_> = Option::Some(1);
            }"#,
            r#"
            enum Option<T> {
                Some(T),
                None
            }

            fn f() {
                let a<|>: Option<i32> = Option::Some(1);
            }"#,
        );
    }

    #[test]
    fn add_explicit_type_works_for_macro_call() {
        check_assist(
            add_explicit_type,
            r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
            r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }",
        );
    }

    #[test]
    fn add_explicit_type_works_for_macro_call_recursive() {
        check_assist(
            add_explicit_type,
            "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }",
            "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }",
        );
    }

    #[test]
    fn add_explicit_type_not_applicable_if_ty_not_inferred() {
        check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }");
    }

    #[test]
    fn add_explicit_type_not_applicable_if_ty_already_specified() {
        check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: i32 = 1; }");
    }

    #[test]
    fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() {
        check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }");
    }

    #[test]
    fn add_explicit_type_not_applicable_if_cursor_after_equals() {
        check_assist_not_applicable(
            add_explicit_type,
            "fn f() {let a =<|> match 1 {2 => 3, 3 => 5};}",
        )
    }

    #[test]
    fn add_explicit_type_not_applicable_if_cursor_before_let() {
        check_assist_not_applicable(
            add_explicit_type,
            "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}",
        )
    }

    #[test]
    fn closure_parameters_are_not_added() {
        check_assist_not_applicable(
            add_explicit_type,
            r#"
fn main() {
    let multiply_by_two<|> = |i| i * 3;
    let six = multiply_by_two(2);
}"#,
        )
    }

    #[test]
    fn default_generics_should_not_be_added() {
        check_assist(
            add_explicit_type,
            r#"
struct Test<K, T = u8> {
    k: K,
    t: T,
}

fn main() {
    let test<|> = Test { t: 23, k: 33 };
}"#,
            r#"
struct Test<K, T = u8> {
    k: K,
    t: T,
}

fn main() {
    let test<|>: Test<i32> = Test { t: 23, k: 33 };
}"#,
        );
    }
}
<commit_msg>Cleanup<commit_after>use hir::HirDisplay;
use ra_syntax::{
    ast::{self, AstNode, LetStmt, NameOwner, TypeAscriptionOwner},
    TextRange,
};

use crate::{AssistContext, AssistId, Assists};

// Assist: add_explicit_type
//
// Specify type for a let binding.
//
// ```
// fn main() {
//     let x<|> = 92;
// }
// ```
// ->
// ```
// fn main() {
//     let x: i32 = 92;
// }
// ```
pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let stmt = ctx.find_node_at_offset::<LetStmt>()?;
    let module = ctx.sema.scope(stmt.syntax()).module()?;
    let expr = stmt.initializer()?;
    // Must be a binding
    let pat = match stmt.pat()? {
        ast::Pat::BindPat(bind_pat) => bind_pat,
        _ => return None,
    };
    let pat_range = pat.syntax().text_range();
    // The binding must have a name
    let name = pat.name()?;
    let name_range = name.syntax().text_range();
    let stmt_range = stmt.syntax().text_range();
    let eq_range = stmt.eq_token()?.text_range();
    // Assist should only be applicable if cursor is between 'let' and '='
    let let_range = TextRange::new(stmt_range.start(), eq_range.start());
    let cursor_in_range = let_range.contains_range(ctx.frange.range);
    if !cursor_in_range {
        return None;
    }
    // Assist not applicable if the type has already been specified
    // and it has no placeholders
    let ascribed_ty = stmt.ascribed_type();
    if let Some(ty) = &ascribed_ty {
        if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
            return None;
        }
    }
    // Infer type
    let ty = ctx.sema.type_of_expr(&expr)?;

    if ty.contains_unknown() || ty.is_closure() {
        return None;
    }

    let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?;
    acc.add(
        AssistId("add_explicit_type"),
        format!("Insert explicit type `{}`", inferred_type),
        pat_range,
        |builder| match ascribed_ty {
            Some(ascribed_ty) => {
                builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
            }
            None => {
                builder.insert(name_range.end(), format!(": {}", inferred_type));
            }
        },
    )
}

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

    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn add_explicit_type_target() {
        check_assist_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a");
    }

    #[test]
    fn add_explicit_type_works_for_simple_expr() {
        check_assist(
            add_explicit_type,
            "fn f() { let a<|> = 1; }",
            "fn f() { let a<|>: i32 = 1; }",
        );
    }

    #[test]
    fn add_explicit_type_works_for_underscore() {
        check_assist(
            add_explicit_type,
            "fn f() { let a<|>: _ = 1; }",
            "fn f() { let a<|>: i32 = 1; }",
        );
    }

    #[test]
    fn add_explicit_type_works_for_nested_underscore() {
        check_assist(
            add_explicit_type,
            r#"
            enum Option<T> {
                Some(T),
                None
            }

            fn f() {
                let a<|>: Option<_> = Option::Some(1);
            }"#,
            r#"
            enum Option<T> {
                Some(T),
                None
            }

            fn f() {
                let a<|>: Option<i32> = Option::Some(1);
            }"#,
        );
    }

    #[test]
    fn add_explicit_type_works_for_macro_call() {
        check_assist(
            add_explicit_type,
            r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
            r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }",
        );
    }

    #[test]
    fn add_explicit_type_works_for_macro_call_recursive() {
        check_assist(
            add_explicit_type,
            "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }",
            "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }",
        );
    }

    #[test]
    fn add_explicit_type_not_applicable_if_ty_not_inferred() {
        check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }");
    }

    #[test]
    fn add_explicit_type_not_applicable_if_ty_already_specified() {
        check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: i32 = 1; }");
    }

    #[test]
    fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() {
        check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }");
    }

    #[test]
    fn add_explicit_type_not_applicable_if_cursor_after_equals() {
        check_assist_not_applicable(
            add_explicit_type,
            "fn f() {let a =<|> match 1 {2 => 3, 3 => 5};}",
        )
    }

    #[test]
    fn add_explicit_type_not_applicable_if_cursor_before_let() {
        check_assist_not_applicable(
            add_explicit_type,
            "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}",
        )
    }

    #[test]
    fn closure_parameters_are_not_added() {
        check_assist_not_applicable(
            add_explicit_type,
            r#"
fn main() {
    let multiply_by_two<|> = |i| i * 3;
    let six = multiply_by_two(2);
}"#,
        )
    }

    #[test]
    fn default_generics_should_not_be_added() {
        check_assist(
            add_explicit_type,
            r#"
struct Test<K, T = u8> {
    k: K,
    t: T,
}

fn main() {
    let test<|> = Test { t: 23, k: 33 };
}"#,
            r#"
struct Test<K, T = u8> {
    k: K,
    t: T,
}

fn main() {
    let test<|>: Test<i32> = Test { t: 23, k: 33 };
}"#,
        );
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use crate::{
    completion::{
        CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
    },
    display::FunctionSignature,
};

use hir::{self, Docs, HasSource};
use ra_syntax::{
    ast::{self, edit},
    AstNode, SyntaxKind, SyntaxNode, TextRange,
};

use ra_assists::utils::get_missing_impl_items;
use ra_text_edit::TextEdit;

pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
    let trigger = ctx.token.ancestors().find(|p| match p.kind() {
        SyntaxKind::FN_DEF | SyntaxKind::TYPE_ALIAS_DEF | SyntaxKind::CONST_DEF => true,
        _ => false,
    });

    let impl_block = trigger
        .as_ref()
        .and_then(|node| node.parent())
        .and_then(|node| node.parent())
        .and_then(|node| ast::ImplBlock::cast(node));

    if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) {
        match trigger.kind() {
            SyntaxKind::FN_DEF => {
                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
                    .iter()
                    .filter_map(|item| match item {
                        hir::AssocItem::Function(fn_item) => Some(fn_item),
                        _ => None,
                    })
                {
                    add_function_impl(&trigger, acc, ctx, &missing_fn);
                }
            }

            SyntaxKind::TYPE_ALIAS_DEF => {
                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
                    .iter()
                    .filter_map(|item| match item {
                        hir::AssocItem::TypeAlias(type_item) => Some(type_item),
                        _ => None,
                    })
                {
                    add_type_alias_impl(&trigger, acc, ctx, &missing_fn);
                }
            }

            SyntaxKind::CONST_DEF => {
                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
                    .iter()
                    .filter_map(|item| match item {
                        hir::AssocItem::Const(const_item) => Some(const_item),
                        _ => None,
                    })
                {
                    add_const_impl(&trigger, acc, ctx, &missing_fn);
                }
            }

            _ => {}
        }
    }
}

fn add_function_impl(
    fn_def_node: &SyntaxNode,
    acc: &mut Completions,
    ctx: &CompletionContext,
    func: &hir::Function,
) {
    let display = FunctionSignature::from_hir(ctx.db, func.clone());

    let func_name = func.name(ctx.db);

    let label = if func.params(ctx.db).len() > 0 {
        format!("fn {}(..)", func_name.to_string())
    } else {
        format!("fn {}()", func_name.to_string())
    };

    let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label.clone())
        .lookup_by(label)
        .set_documentation(func.docs(ctx.db));

    let completion_kind = if func.has_self_param(ctx.db) {
        CompletionItemKind::Method
    } else {
        CompletionItemKind::Function
    };

    let snippet = format!("{} {{}}", display);

    builder
        .text_edit(TextEdit::replace(fn_def_node.text_range(), snippet))
        .kind(completion_kind)
        .add_to(acc);
}

fn add_type_alias_impl(
    type_def_node: &SyntaxNode,
    acc: &mut Completions,
    ctx: &CompletionContext,
    type_alias: &hir::TypeAlias,
) {
    let snippet = format!("type {} = ", type_alias.name(ctx.db).to_string());

    CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
        .text_edit(TextEdit::replace(type_def_node.text_range(), snippet))
        .kind(CompletionItemKind::TypeAlias)
        .set_documentation(type_alias.docs(ctx.db))
        .add_to(acc);
}

fn add_const_impl(
    const_def_node: &SyntaxNode,
    acc: &mut Completions,
    ctx: &CompletionContext,
    const_: &hir::Const,
) {
    let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);

    CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
        .text_edit(TextEdit::replace(const_def_node.text_range(), snippet))
        .kind(CompletionItemKind::Const)
        .set_documentation(const_.docs(ctx.db))
        .add_to(acc);
}

fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
    let const_ = edit::strip_attrs_and_docs(const_);

    let const_start = const_.syntax().text_range().start();
    let const_end = const_.syntax().text_range().end();

    let start =
        const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());

    let end = const_
        .syntax()
        .children_with_tokens()
        .find(|s| s.kind() == SyntaxKind::SEMI || s.kind() == SyntaxKind::EQ)
        .map_or(const_end, |f| f.text_range().start());

    let len = end - start;
    let range = TextRange::from_to(0.into(), len);

    let syntax = const_.syntax().text().slice(range).to_string();

    format!("{} = ", syntax.trim_end())
}

#[cfg(test)]
mod tests {
    use crate::completion::{do_completion, CompletionItem, CompletionKind};
    use insta::assert_debug_snapshot;

    fn complete(code: &str) -> Vec<CompletionItem> {
        do_completion(code, CompletionKind::Magic)
    }

    #[test]
    fn single_function() {
        let completions = complete(
            r"
            trait Test {
                fn foo();
            }

            struct T1;

            impl Test for T1 {
                fn<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "fn foo()",
                source_range: [140; 140),
                delete: [138; 140),
                insert: "fn foo() {}",
                kind: Function,
            },
        ]
        "###);
    }

    #[test]
    fn hide_implemented_fn() {
        let completions = complete(
            r"
            trait Test {
                fn foo();
                fn bar();
            }

            struct T1;

            impl Test for T1 {
                fn foo() {}

                fn<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "fn bar()",
                source_range: [195; 195),
                delete: [193; 195),
                insert: "fn bar() {}",
                kind: Function,
            },
        ]
        "###);
    }

    #[test]
    fn generic_fn() {
        let completions = complete(
            r"
            trait Test {
                fn foo<T>();
            }

            struct T1;

            impl Test for T1 {
                fn<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "fn foo()",
                source_range: [143; 143),
                delete: [141; 143),
                insert: "fn foo<T>() {}",
                kind: Function,
            },
        ]
        "###);
    }

    #[test]
    fn generic_constrait_fn() {
        let completions = complete(
            r"
            trait Test {
                fn foo<T>() where T: Into<String>;
            }

            struct T1;

            impl Test for T1 {
                fn<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "fn foo()",
                source_range: [165; 165),
                delete: [163; 165),
                insert: "fn foo<T>()\nwhere T: Into<String> {}",
                kind: Function,
            },
        ]
        "###);
    }

    #[test]
    fn associated_type() {
        let completions = complete(
            r"
            trait Test {
                type SomeType;
            }

            impl Test for () {
                type<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "type SomeType = ",
                source_range: [123; 123),
                delete: [119; 123),
                insert: "type SomeType = ",
                kind: TypeAlias,
            },
        ]
        "###);
    }

    #[test]
    fn associated_const() {
        let completions = complete(
            r"
            trait Test {
                const SOME_CONST: u16;
            }

            impl Test for () {
                const S<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "const SOME_CONST: u16 = ",
                source_range: [133; 134),
                delete: [127; 134),
                insert: "const SOME_CONST: u16 = ",
                kind: Const,
            },
        ]
        "###);
    }

    #[test]
    fn associated_const_with_default() {
        let completions = complete(
            r"
            trait Test {
                const SOME_CONST: u16 = 42;
            }

            impl Test for () {
                const S<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "const SOME_CONST: u16 = ",
                source_range: [138; 139),
                delete: [132; 139),
                insert: "const SOME_CONST: u16 = ",
                kind: Const,
            },
        ]
        "###);
    }
}
<commit_msg>Adjusted the completion lookups to filter by just the name.<commit_after>//! FIXME: write short doc here

use crate::{
    completion::{
        CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
    },
    display::FunctionSignature,
};

use hir::{self, Docs, HasSource};
use ra_syntax::{
    ast::{self, edit},
    AstNode, SyntaxKind, SyntaxNode, TextRange,
};

use ra_assists::utils::get_missing_impl_items;
use ra_text_edit::TextEdit;

pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
    let trigger = ctx.token.ancestors().find(|p| match p.kind() {
        SyntaxKind::FN_DEF | SyntaxKind::TYPE_ALIAS_DEF | SyntaxKind::CONST_DEF => true,
        _ => false,
    });

    let impl_block = trigger
        .as_ref()
        .and_then(|node| node.parent())
        .and_then(|node| node.parent())
        .and_then(|node| ast::ImplBlock::cast(node));

    if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) {
        match trigger.kind() {
            SyntaxKind::FN_DEF => {
                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
                    .iter()
                    .filter_map(|item| match item {
                        hir::AssocItem::Function(fn_item) => Some(fn_item),
                        _ => None,
                    })
                {
                    add_function_impl(&trigger, acc, ctx, &missing_fn);
                }
            }

            SyntaxKind::TYPE_ALIAS_DEF => {
                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
                    .iter()
                    .filter_map(|item| match item {
                        hir::AssocItem::TypeAlias(type_item) => Some(type_item),
                        _ => None,
                    })
                {
                    add_type_alias_impl(&trigger, acc, ctx, &missing_fn);
                }
            }

            SyntaxKind::CONST_DEF => {
                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
                    .iter()
                    .filter_map(|item| match item {
                        hir::AssocItem::Const(const_item) => Some(const_item),
                        _ => None,
                    })
                {
                    add_const_impl(&trigger, acc, ctx, &missing_fn);
                }
            }

            _ => {}
        }
    }
}

fn add_function_impl(
    fn_def_node: &SyntaxNode,
    acc: &mut Completions,
    ctx: &CompletionContext,
    func: &hir::Function,
) {
    let display = FunctionSignature::from_hir(ctx.db, func.clone());

    let fn_name = func.name(ctx.db).to_string();

    let label = if func.params(ctx.db).len() > 0 {
        format!("fn {}(..)", fn_name)
    } else {
        format!("fn {}()", fn_name)
    };

    let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label.clone())
        .lookup_by(fn_name)
        .set_documentation(func.docs(ctx.db));

    let completion_kind = if func.has_self_param(ctx.db) {
        CompletionItemKind::Method
    } else {
        CompletionItemKind::Function
    };

    let snippet = format!("{} {{}}", display);

    builder
        .text_edit(TextEdit::replace(fn_def_node.text_range(), snippet))
        .kind(completion_kind)
        .add_to(acc);
}

fn add_type_alias_impl(
    type_def_node: &SyntaxNode,
    acc: &mut Completions,
    ctx: &CompletionContext,
    type_alias: &hir::TypeAlias,
) {
    let alias_name = type_alias.name(ctx.db).to_string();

    let snippet = format!("type {} = ", alias_name);

    CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
        .text_edit(TextEdit::replace(type_def_node.text_range(), snippet))
        .lookup_by(alias_name)
        .kind(CompletionItemKind::TypeAlias)
        .set_documentation(type_alias.docs(ctx.db))
        .add_to(acc);
}

fn add_const_impl(
    const_def_node: &SyntaxNode,
    acc: &mut Completions,
    ctx: &CompletionContext,
    const_: &hir::Const,
) {
    let const_name = const_.name(ctx.db).map(|n| n.to_string());

    if let Some(const_name) = const_name {
        let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);

        CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
            .text_edit(TextEdit::replace(const_def_node.text_range(), snippet))
            .lookup_by(const_name)
            .kind(CompletionItemKind::Const)
            .set_documentation(const_.docs(ctx.db))
            .add_to(acc);
    }
}

fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
    let const_ = edit::strip_attrs_and_docs(const_);

    let const_start = const_.syntax().text_range().start();
    let const_end = const_.syntax().text_range().end();

    let start =
        const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());

    let end = const_
        .syntax()
        .children_with_tokens()
        .find(|s| s.kind() == SyntaxKind::SEMI || s.kind() == SyntaxKind::EQ)
        .map_or(const_end, |f| f.text_range().start());

    let len = end - start;
    let range = TextRange::from_to(0.into(), len);

    let syntax = const_.syntax().text().slice(range).to_string();

    format!("{} = ", syntax.trim_end())
}

#[cfg(test)]
mod tests {
    use crate::completion::{do_completion, CompletionItem, CompletionKind};
    use insta::assert_debug_snapshot;

    fn complete(code: &str) -> Vec<CompletionItem> {
        do_completion(code, CompletionKind::Magic)
    }

    #[test]
    fn single_function() {
        let completions = complete(
            r"
            trait Test {
                fn foo();
            }

            struct T1;

            impl Test for T1 {
                fn f<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "fn foo()",
                source_range: [141; 142),
                delete: [138; 142),
                insert: "fn foo() {}",
                kind: Function,
                lookup: "foo",
            },
        ]
        "###);
    }

    #[test]
    fn hide_implemented_fn() {
        let completions = complete(
            r"
            trait Test {
                fn foo();
                fn foo_bar();
            }

            struct T1;

            impl Test for T1 {
                fn foo() {}

                fn f<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "fn foo_bar()",
                source_range: [200; 201),
                delete: [197; 201),
                insert: "fn foo_bar() {}",
                kind: Function,
                lookup: "foo_bar",
            },
        ]
        "###);
    }

    #[test]
    fn generic_fn() {
        let completions = complete(
            r"
            trait Test {
                fn foo<T>();
            }

            struct T1;

            impl Test for T1 {
                fn f<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "fn foo()",
                source_range: [144; 145),
                delete: [141; 145),
                insert: "fn foo<T>() {}",
                kind: Function,
                lookup: "foo",
            },
        ]
        "###);
    }

    #[test]
    fn generic_constrait_fn() {
        let completions = complete(
            r"
            trait Test {
                fn foo<T>() where T: Into<String>;
            }

            struct T1;

            impl Test for T1 {
                fn f<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "fn foo()",
                source_range: [166; 167),
                delete: [163; 167),
                insert: "fn foo<T>()\nwhere T: Into<String> {}",
                kind: Function,
                lookup: "foo",
            },
        ]
        "###);
    }

    #[test]
    fn associated_type() {
        let completions = complete(
            r"
            trait Test {
                type SomeType;
            }

            impl Test for () {
                type S<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "type SomeType = ",
                source_range: [124; 125),
                delete: [119; 125),
                insert: "type SomeType = ",
                kind: TypeAlias,
                lookup: "SomeType",
            },
        ]
        "###);
    }

    #[test]
    fn associated_const() {
        let completions = complete(
            r"
            trait Test {
                const SOME_CONST: u16;
            }

            impl Test for () {
                const S<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "const SOME_CONST: u16 = ",
                source_range: [133; 134),
                delete: [127; 134),
                insert: "const SOME_CONST: u16 = ",
                kind: Const,
                lookup: "SOME_CONST",
            },
        ]
        "###);
    }

    #[test]
    fn associated_const_with_default() {
        let completions = complete(
            r"
            trait Test {
                const SOME_CONST: u16 = 42;
            }

            impl Test for () {
                const S<|>
            }
            ",
        );
        assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "const SOME_CONST: u16 = ",
                source_range: [138; 139),
                delete: [132; 139),
                insert: "const SOME_CONST: u16 = ",
                kind: Const,
                lookup: "SOME_CONST",
            },
        ]
        "###);
    }
}
<|endoftext|>


<commit_before>//! Handles dynamic library loading for proc macro

use crate::{proc_macro::bridge, rustc_server::TokenStream};
use std::path::Path;

use goblin::{mach::Mach, Object};
use libloading::Library;
use ra_proc_macro::ProcMacroKind;

use std::io::Error as IoError;
use std::io::ErrorKind as IoErrorKind;

const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";

fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> IoError {
    IoError::new(IoErrorKind::InvalidData, e)
}

fn get_symbols_from_lib(file: &Path) -> Result<Vec<String>, IoError> {
    let buffer = std::fs::read(file)?;
    let object = Object::parse(&buffer).map_err(invalid_data_err)?;

    match object {
        Object::Elf(elf) => {
            let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
            let names = symbols.iter().map(|s| s.to_string()).collect();
            Ok(names)
        }
        Object::PE(pe) => {
            let symbol_names =
                pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect();
            Ok(symbol_names)
        }
        Object::Mach(mach) => match mach {
            Mach::Binary(binary) => {
                let exports = binary.exports().map_err(invalid_data_err)?;
                let names = exports
                    .into_iter()
                    .map(|s| {
                        // In macos doc:
                        // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
                        // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
                        // prepended with an underscore.
                        if s.name.starts_with("_") {
                            s.name[1..].to_string()
                        } else {
                            s.name
                        }
                    })
                    .collect();
                Ok(names)
            }
            Mach::Fat(_) => Ok(vec![]),
        },
        Object::Archive(_) | Object::Unknown(_) => Ok(vec![]),
    }
}

fn is_derive_registrar_symbol(symbol: &str) -> bool {
    symbol.contains(NEW_REGISTRAR_SYMBOL)
}

fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
    let symbols = get_symbols_from_lib(file)?;
    Ok(symbols.into_iter().find(|s| is_derive_registrar_symbol(s)))
}

/// Loads dynamic library in platform dependent manner.
///
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
/// and [here](https://github.com/rust-lang/rust/issues/60593).
///
/// Usage of RTLD_DEEPBIND
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
///
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
#[cfg(windows)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    Library::new(file)
}

#[cfg(unix)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    use libloading::os::unix::Library as UnixLibrary;
    use std::os::raw::c_int;

    const RTLD_NOW: c_int = 0x00002;
    const RTLD_DEEPBIND: c_int = 0x00008;

    UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into())
}

struct ProcMacroLibraryLibloading {
    // Hold the dylib to prevent it for unloadeding
    _lib: Library,
    exported_macros: Vec<bridge::client::ProcMacro>,
}

impl ProcMacroLibraryLibloading {
    fn open(file: &Path) -> Result<Self, IoError> {
        let symbol_name = find_registrar_symbol(file)?
            .ok_or(invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file)))?;

        let lib = load_library(file).map_err(invalid_data_err)?;
        let exported_macros = {
            let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> =
                unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
            macros.to_vec()
        };

        Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros })
    }
}

type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;

pub struct Expander {
    libs: Vec<ProcMacroLibraryImpl>,
}

impl Expander {
    pub fn new<P: AsRef<Path>>(lib: &P) -> Result<Expander, String> {
        let mut libs = vec![];
        /* Some libraries for dynamic loading require canonicalized path (even when it is
        already absolute
        */
        let lib =
            lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref()));

        let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
        libs.push(library);

        Ok(Expander { libs })
    }

    pub fn expand(
        &self,
        macro_name: &str,
        macro_body: &ra_tt::Subtree,
        attributes: Option<&ra_tt::Subtree>,
    ) -> Result<ra_tt::Subtree, bridge::PanicMessage> {
        let parsed_body = TokenStream::with_subtree(macro_body.clone());

        let parsed_attributes = attributes
            .map_or(crate::rustc_server::TokenStream::new(), |attr| {
                TokenStream::with_subtree(attr.clone())
            });

        for lib in &self.libs {
            for proc_macro in &lib.exported_macros {
                match proc_macro {
                    bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
                        if *trait_name == macro_name =>
                    {
                        let res = client.run(
                            &crate::proc_macro::bridge::server::SameThread,
                            crate::rustc_server::Rustc::default(),
                            parsed_body,
                        );
                        return res.map(|it| it.subtree);
                    }
                    bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
                        let res = client.run(
                            &crate::proc_macro::bridge::server::SameThread,
                            crate::rustc_server::Rustc::default(),
                            parsed_body,
                        );
                        return res.map(|it| it.subtree);
                    }
                    bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
                        let res = client.run(
                            &crate::proc_macro::bridge::server::SameThread,
                            crate::rustc_server::Rustc::default(),
                            parsed_attributes,
                            parsed_body,
                        );

                        return res.map(|it| it.subtree);
                    }
                    _ => continue,
                }
            }
        }

        Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
    }

    pub fn list_macros(&self) -> Result<Vec<(String, ProcMacroKind)>, bridge::PanicMessage> {
        let mut result = vec![];

        for lib in &self.libs {
            for proc_macro in &lib.exported_macros {
                let res = match proc_macro {
                    bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                        (trait_name.to_string(), ProcMacroKind::CustomDerive)
                    }
                    bridge::client::ProcMacro::Bang { name, .. } => {
                        (name.to_string(), ProcMacroKind::FuncLike)
                    }
                    bridge::client::ProcMacro::Attr { name, .. } => {
                        (name.to_string(), ProcMacroKind::Attr)
                    }
                };
                result.push(res);
            }
        }

        Ok(result)
    }
}
<commit_msg>Reduce allocations when looking up proc macro decl<commit_after>//! Handles dynamic library loading for proc macro

use crate::{proc_macro::bridge, rustc_server::TokenStream};
use std::path::Path;

use goblin::{mach::Mach, Object};
use libloading::Library;
use ra_proc_macro::ProcMacroKind;

use std::io::Error as IoError;
use std::io::ErrorKind as IoErrorKind;

const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";

fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> IoError {
    IoError::new(IoErrorKind::InvalidData, e)
}

fn is_derive_registrar_symbol(symbol: &str) -> bool {
    symbol.contains(NEW_REGISTRAR_SYMBOL)
}

fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
    let buffer = std::fs::read(file)?;
    let object = Object::parse(&buffer).map_err(invalid_data_err)?;

    match object {
        Object::Elf(elf) => {
            let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
            let name =
                symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string());
            Ok(name)
        }
        Object::PE(pe) => {
            let name = pe
                .exports
                .iter()
                .flat_map(|s| s.name)
                .find(|s| is_derive_registrar_symbol(s))
                .map(|s| s.to_string());
            Ok(name)
        }
        Object::Mach(Mach::Binary(binary)) => {
            let exports = binary.exports().map_err(invalid_data_err)?;
            let name = exports
                .iter()
                .map(|s| {
                    // In macos doc:
                    // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
                    // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
                    // prepended with an underscore.
                    if s.name.starts_with("_") {
                        &s.name[1..]
                    } else {
                        &s.name
                    }
                })
                .find(|s| is_derive_registrar_symbol(&s))
                .map(|s| s.to_string());
            Ok(name)
        }
        _ => Ok(None),
    }
}

/// Loads dynamic library in platform dependent manner.
///
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
/// and [here](https://github.com/rust-lang/rust/issues/60593).
///
/// Usage of RTLD_DEEPBIND
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
///
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
#[cfg(windows)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    Library::new(file)
}

#[cfg(unix)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    use libloading::os::unix::Library as UnixLibrary;
    use std::os::raw::c_int;

    const RTLD_NOW: c_int = 0x00002;
    const RTLD_DEEPBIND: c_int = 0x00008;

    UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into())
}

struct ProcMacroLibraryLibloading {
    // Hold the dylib to prevent it for unloadeding
    _lib: Library,
    exported_macros: Vec<bridge::client::ProcMacro>,
}

impl ProcMacroLibraryLibloading {
    fn open(file: &Path) -> Result<Self, IoError> {
        let symbol_name = find_registrar_symbol(file)?
            .ok_or(invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file)))?;

        let lib = load_library(file).map_err(invalid_data_err)?;
        let exported_macros = {
            let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> =
                unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
            macros.to_vec()
        };

        Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros })
    }
}

type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;

pub struct Expander {
    libs: Vec<ProcMacroLibraryImpl>,
}

impl Expander {
    pub fn new<P: AsRef<Path>>(lib: &P) -> Result<Expander, String> {
        let mut libs = vec![];
        /* Some libraries for dynamic loading require canonicalized path (even when it is
        already absolute
        */
        let lib =
            lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref()));

        let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
        libs.push(library);

        Ok(Expander { libs })
    }

    pub fn expand(
        &self,
        macro_name: &str,
        macro_body: &ra_tt::Subtree,
        attributes: Option<&ra_tt::Subtree>,
    ) -> Result<ra_tt::Subtree, bridge::PanicMessage> {
        let parsed_body = TokenStream::with_subtree(macro_body.clone());

        let parsed_attributes = attributes
            .map_or(crate::rustc_server::TokenStream::new(), |attr| {
                TokenStream::with_subtree(attr.clone())
            });

        for lib in &self.libs {
            for proc_macro in &lib.exported_macros {
                match proc_macro {
                    bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
                        if *trait_name == macro_name =>
                    {
                        let res = client.run(
                            &crate::proc_macro::bridge::server::SameThread,
                            crate::rustc_server::Rustc::default(),
                            parsed_body,
                        );
                        return res.map(|it| it.subtree);
                    }
                    bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
                        let res = client.run(
                            &crate::proc_macro::bridge::server::SameThread,
                            crate::rustc_server::Rustc::default(),
                            parsed_body,
                        );
                        return res.map(|it| it.subtree);
                    }
                    bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
                        let res = client.run(
                            &crate::proc_macro::bridge::server::SameThread,
                            crate::rustc_server::Rustc::default(),
                            parsed_attributes,
                            parsed_body,
                        );

                        return res.map(|it| it.subtree);
                    }
                    _ => continue,
                }
            }
        }

        Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
    }

    pub fn list_macros(&self) -> Result<Vec<(String, ProcMacroKind)>, bridge::PanicMessage> {
        let mut result = vec![];

        for lib in &self.libs {
            for proc_macro in &lib.exported_macros {
                let res = match proc_macro {
                    bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                        (trait_name.to_string(), ProcMacroKind::CustomDerive)
                    }
                    bridge::client::ProcMacro::Bang { name, .. } => {
                        (name.to_string(), ProcMacroKind::FuncLike)
                    }
                    bridge::client::ProcMacro::Attr { name, .. } => {
                        (name.to_string(), ProcMacroKind::Attr)
                    }
                };
                result.push(res);
            }
        }

        Ok(result)
    }
}
<|endoftext|>


<commit_before>//! Common types for [encryption] related tasks.
//!
//! [encryption]: https://spec.matrix.org/v1.2/client-server-api/#end-to-end-encryption

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use crate::{serde::Base64, DeviceId, DeviceKeyId, EventEncryptionAlgorithm, UserId};

/// Identity keys for a device.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct DeviceKeys {
    /// The ID of the user the device belongs to.
    ///
    /// Must match the user ID used when logging in.
    pub user_id: Box<UserId>,

    /// The ID of the device these keys belong to.
    ///
    /// Must match the device ID used when logging in.
    pub device_id: Box<DeviceId>,

    /// The encryption algorithms supported by this device.
    pub algorithms: Vec<EventEncryptionAlgorithm>,

    /// Public identity keys.
    pub keys: BTreeMap<Box<DeviceKeyId>, String>,

    /// Signatures for the device key object.
    pub signatures: BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>,

    /// Additional data added to the device key information by intermediate servers, and
    /// not covered by the signatures.
    #[serde(default, skip_serializing_if = "UnsignedDeviceInfo::is_empty")]
    pub unsigned: UnsignedDeviceInfo,
}

impl DeviceKeys {
    /// Creates a new `DeviceKeys` from the given user id, device id, algorithms, keys and
    /// signatures.
    pub fn new(
        user_id: Box<UserId>,
        device_id: Box<DeviceId>,
        algorithms: Vec<EventEncryptionAlgorithm>,
        keys: BTreeMap<Box<DeviceKeyId>, String>,
        signatures: BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>,
    ) -> Self {
        Self { user_id, device_id, algorithms, keys, signatures, unsigned: Default::default() }
    }
}

/// Additional data added to device key information by intermediate servers.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct UnsignedDeviceInfo {
    /// The display name which the user set on the device.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub device_display_name: Option<String>,
}

impl UnsignedDeviceInfo {
    /// Creates an empty `UnsignedDeviceInfo`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Checks whether all fields are empty / `None`.
    pub fn is_empty(&self) -> bool {
        self.device_display_name.is_none()
    }
}

/// Signatures for a `SignedKey` object.
pub type SignedKeySignatures = BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>;

/// A key for the SignedCurve25519 algorithm
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct SignedKey {
    /// Base64-encoded 32-byte Curve25519 public key.
    pub key: Base64,

    /// Signatures for the key object.
    pub signatures: SignedKeySignatures,

    /// Is this key considered to be a fallback key, defaults to false.
    #[serde(default, skip_serializing_if = "crate::serde::is_default")]
    pub fallback: bool,
}

impl SignedKey {
    /// Creates a new `SignedKey` with the given key and signatures.
    pub fn new(key: Base64, signatures: SignedKeySignatures) -> Self {
        Self { key, signatures, fallback: false }
    }

    /// Creates a new fallback `SignedKey` with the given key and signatures.
    pub fn new_fallback(key: Base64, signatures: SignedKeySignatures) -> Self {
        Self { key, signatures, fallback: true }
    }
}

/// A one-time public key for "pre-key" messages.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(untagged)]
pub enum OneTimeKey {
    /// A key containing signatures, for the SignedCurve25519 algorithm.
    SignedKey(SignedKey),

    /// A string-valued key, for the Ed25519 and Curve25519 algorithms.
    Key(String),
}

/// Signatures for a `CrossSigningKey` object.
pub type CrossSigningKeySignatures = BTreeMap<Box<UserId>, BTreeMap<String, String>>;

/// A cross signing key.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct CrossSigningKey {
    /// The ID of the user the key belongs to.
    pub user_id: Box<UserId>,

    /// What the key is used for.
    pub usage: Vec<KeyUsage>,

    /// The public key.
    ///
    /// The object must have exactly one property.
    pub keys: BTreeMap<String, String>,

    /// Signatures of the key.
    ///
    /// Only optional for master key.
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub signatures: CrossSigningKeySignatures,
}

impl CrossSigningKey {
    /// Creates a new `CrossSigningKey` with the given user ID, usage, keys and signatures.
    pub fn new(
        user_id: Box<UserId>,
        usage: Vec<KeyUsage>,
        keys: BTreeMap<String, String>,
        signatures: CrossSigningKeySignatures,
    ) -> Self {
        Self { user_id, usage, keys, signatures }
    }
}

/// The usage of a cross signing key.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(rename_all = "snake_case")]
pub enum KeyUsage {
    /// Master key.
    Master,

    /// Self-signing key.
    SelfSigning,

    /// User-signing key.
    UserSigning,
}
<commit_msg>common: Use StringEnum for the KeyUsage enum<commit_after>//! Common types for [encryption] related tasks.
//!
//! [encryption]: https://spec.matrix.org/v1.2/client-server-api/#end-to-end-encryption

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use crate::{
    serde::{Base64, StringEnum},
    DeviceId, DeviceKeyId, EventEncryptionAlgorithm, PrivOwnedStr, UserId,
};

/// Identity keys for a device.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct DeviceKeys {
    /// The ID of the user the device belongs to.
    ///
    /// Must match the user ID used when logging in.
    pub user_id: Box<UserId>,

    /// The ID of the device these keys belong to.
    ///
    /// Must match the device ID used when logging in.
    pub device_id: Box<DeviceId>,

    /// The encryption algorithms supported by this device.
    pub algorithms: Vec<EventEncryptionAlgorithm>,

    /// Public identity keys.
    pub keys: BTreeMap<Box<DeviceKeyId>, String>,

    /// Signatures for the device key object.
    pub signatures: BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>,

    /// Additional data added to the device key information by intermediate servers, and
    /// not covered by the signatures.
    #[serde(default, skip_serializing_if = "UnsignedDeviceInfo::is_empty")]
    pub unsigned: UnsignedDeviceInfo,
}

impl DeviceKeys {
    /// Creates a new `DeviceKeys` from the given user id, device id, algorithms, keys and
    /// signatures.
    pub fn new(
        user_id: Box<UserId>,
        device_id: Box<DeviceId>,
        algorithms: Vec<EventEncryptionAlgorithm>,
        keys: BTreeMap<Box<DeviceKeyId>, String>,
        signatures: BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>,
    ) -> Self {
        Self { user_id, device_id, algorithms, keys, signatures, unsigned: Default::default() }
    }
}

/// Additional data added to device key information by intermediate servers.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct UnsignedDeviceInfo {
    /// The display name which the user set on the device.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub device_display_name: Option<String>,
}

impl UnsignedDeviceInfo {
    /// Creates an empty `UnsignedDeviceInfo`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Checks whether all fields are empty / `None`.
    pub fn is_empty(&self) -> bool {
        self.device_display_name.is_none()
    }
}

/// Signatures for a `SignedKey` object.
pub type SignedKeySignatures = BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>;

/// A key for the SignedCurve25519 algorithm
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct SignedKey {
    /// Base64-encoded 32-byte Curve25519 public key.
    pub key: Base64,

    /// Signatures for the key object.
    pub signatures: SignedKeySignatures,

    /// Is this key considered to be a fallback key, defaults to false.
    #[serde(default, skip_serializing_if = "crate::serde::is_default")]
    pub fallback: bool,
}

impl SignedKey {
    /// Creates a new `SignedKey` with the given key and signatures.
    pub fn new(key: Base64, signatures: SignedKeySignatures) -> Self {
        Self { key, signatures, fallback: false }
    }

    /// Creates a new fallback `SignedKey` with the given key and signatures.
    pub fn new_fallback(key: Base64, signatures: SignedKeySignatures) -> Self {
        Self { key, signatures, fallback: true }
    }
}

/// A one-time public key for "pre-key" messages.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(untagged)]
pub enum OneTimeKey {
    /// A key containing signatures, for the SignedCurve25519 algorithm.
    SignedKey(SignedKey),

    /// A string-valued key, for the Ed25519 and Curve25519 algorithms.
    Key(String),
}

/// Signatures for a `CrossSigningKey` object.
pub type CrossSigningKeySignatures = BTreeMap<Box<UserId>, BTreeMap<String, String>>;

/// A cross signing key.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct CrossSigningKey {
    /// The ID of the user the key belongs to.
    pub user_id: Box<UserId>,

    /// What the key is used for.
    pub usage: Vec<KeyUsage>,

    /// The public key.
    ///
    /// The object must have exactly one property.
    pub keys: BTreeMap<String, String>,

    /// Signatures of the key.
    ///
    /// Only optional for master key.
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub signatures: CrossSigningKeySignatures,
}

impl CrossSigningKey {
    /// Creates a new `CrossSigningKey` with the given user ID, usage, keys and signatures.
    pub fn new(
        user_id: Box<UserId>,
        usage: Vec<KeyUsage>,
        keys: BTreeMap<String, String>,
        signatures: CrossSigningKeySignatures,
    ) -> Self {
        Self { user_id, usage, keys, signatures }
    }
}

/// The usage of a cross signing key.
#[derive(Clone, Debug, StringEnum)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_enum(rename_all = "snake_case")]
pub enum KeyUsage {
    /// Master key.
    Master,

    /// Self-signing key.
    SelfSigning,

    /// User-signing key.
    UserSigning,

    #[doc(hidden)]
    _Custom(PrivOwnedStr),
}
<|endoftext|>


<commit_before>use std::io::{stdout, stderr, Write, Result};

//use term_utils;

fn write_to_stdout(buf: String)-> Result<()>{
    let mut output = stdout();
    try!(output.write(buf.as_bytes()));
    try!(output.flush());
    Ok(())
}

fn write_to_stderr(buf: String) -> Result<()> {
    let mut output = stderr();
    try!(output.write(buf.as_bytes()));
    try!(output.flush());
    Ok(())
}

///all progressbars will implement it
pub trait ProgressBar<T> {
    fn new() -> T;
    fn to_stderr(mut self) -> T;
    fn write(&self, buf: String) -> Result<()>;
}

pub struct PercentageProgressBar {
    value: u8, //0..100
    msg:   String,
    write_fn: fn(String) -> Result<()>,
}

impl ProgressBar<PercentageProgressBar> for PercentageProgressBar {

    fn new() -> PercentageProgressBar {
        PercentageProgressBar { value: 0, msg: "".to_owned(),  write_fn: write_to_stdout}
    }

    fn to_stderr(mut self) ->  PercentageProgressBar{
        self.write_fn = write_to_stderr;
        self
    }

    fn write(&self, buf: String) -> Result<()> {
        try!((self.write_fn)(buf));
        Ok(())
    }

}

impl PercentageProgressBar {

    pub fn render(&mut self) -> Result<()> {

        let s:String = format!("\r{msg}{value}%", msg=self.msg, value=self.value);
        try!(self.write(s));
        Ok(())
    }

    pub fn set_value(&mut self, value: u8) { if value <= 100 { self.value = value } }
    pub fn get_value(&self) -> u8 { self.value }

    pub fn set_msg(&mut self, msg: &str) { self.msg = msg.to_owned() }
    pub fn get_msg(&self) -> &str { self.msg.as_ref() }

    pub fn inc(&mut self) { if self.value < 100 { self.value += 1; } }
    pub fn dec(&mut self) { if self.value > 0 { self.value -= 1; } }

}

pub struct InfiniteProgressBar {
    msg:   String,
    marker_position:  i8,
    step: i8,
    write_fn: fn(String) -> Result<()>,
}

impl Default for InfiniteProgressBar {
    fn default() -> InfiniteProgressBar {
        InfiniteProgressBar {
            step: 1,
            msg: "".to_owned(),
            marker_position: 0,
            write_fn: write_to_stdout
        }
    }
}

impl ProgressBar<InfiniteProgressBar> for InfiniteProgressBar {

    fn new() -> InfiniteProgressBar {
        InfiniteProgressBar { ..Default::default()}
    }

    fn to_stderr(mut self) -> InfiniteProgressBar {
        self.write_fn = write_to_stderr;
        self
    }

    fn write(&self, buf: String) -> Result<()> {
        try!((self.write_fn)(buf));
        Ok(())
    }

}

impl InfiniteProgressBar {

    pub fn set_msg(&mut self, msg: &str) { self.msg = msg.to_owned() }
    pub fn get_msg(&self) -> &str { self.msg.as_ref() }

    pub fn render(&mut self) -> Result<()> {

        //let (screen_w, screen_h) = term_utils::get_winsize().unwrap();

        if self.marker_position <= 0 {
            self.marker_position = 0;
            self.step = 1;
        } else if self.marker_position > 9 {
            self.marker_position = 10;
            self.step = -1;
        }
        self.marker_position = self.marker_position + self.step;

        let mut bar:String = "..........".to_owned(); //10 dots
        bar.insert(self.marker_position as usize, '#');

        try!(self.write(format!("\r{msg}[{bar}]", msg=self.msg, bar=bar)));
        Ok(())

    }

}
<commit_msg>Add a space between msg and progressbar<commit_after>use std::io::{stdout, stderr, Write, Result};

//use term_utils;

fn write_to_stdout(buf: String)-> Result<()>{
    let mut output = stdout();
    try!(output.write(buf.as_bytes()));
    try!(output.flush());
    Ok(())
}

fn write_to_stderr(buf: String) -> Result<()> {
    let mut output = stderr();
    try!(output.write(buf.as_bytes()));
    try!(output.flush());
    Ok(())
}

///all progressbars will implement it
pub trait ProgressBar<T> {
    fn new() -> T;
    fn to_stderr(mut self) -> T;
    fn write(&self, buf: String) -> Result<()>;
}

pub struct PercentageProgressBar {
    value: u8, //0..100
    msg:   String,
    write_fn: fn(String) -> Result<()>,
}

impl ProgressBar<PercentageProgressBar> for PercentageProgressBar {

    fn new() -> PercentageProgressBar {
        PercentageProgressBar { value: 0, msg: "".to_owned(),  write_fn: write_to_stdout}
    }

    fn to_stderr(mut self) ->  PercentageProgressBar{
        self.write_fn = write_to_stderr;
        self
    }

    fn write(&self, buf: String) -> Result<()> {
        try!((self.write_fn)(buf));
        Ok(())
    }

}

impl PercentageProgressBar {

    pub fn render(&mut self) -> Result<()> {

        let s:String = format!("\r{msg} {value}%", msg=self.msg, value=self.value);
        try!(self.write(s));
        Ok(())
    }

    pub fn set_value(&mut self, value: u8) { if value <= 100 { self.value = value } }
    pub fn get_value(&self) -> u8 { self.value }

    pub fn set_msg(&mut self, msg: &str) { self.msg = msg.to_owned() }
    pub fn get_msg(&self) -> &str { self.msg.as_ref() }

    pub fn inc(&mut self) { if self.value < 100 { self.value += 1; } }
    pub fn dec(&mut self) { if self.value > 0 { self.value -= 1; } }

}

pub struct InfiniteProgressBar {
    msg:   String,
    marker_position:  i8,
    step: i8,
    write_fn: fn(String) -> Result<()>,
}

impl Default for InfiniteProgressBar {
    fn default() -> InfiniteProgressBar {
        InfiniteProgressBar {
            step: 1,
            msg: "".to_owned(),
            marker_position: 0,
            write_fn: write_to_stdout
        }
    }
}

impl ProgressBar<InfiniteProgressBar> for InfiniteProgressBar {

    fn new() -> InfiniteProgressBar {
        InfiniteProgressBar { ..Default::default()}
    }

    fn to_stderr(mut self) -> InfiniteProgressBar {
        self.write_fn = write_to_stderr;
        self
    }

    fn write(&self, buf: String) -> Result<()> {
        try!((self.write_fn)(buf));
        Ok(())
    }

}

impl InfiniteProgressBar {

    pub fn set_msg(&mut self, msg: &str) { self.msg = msg.to_owned() }
    pub fn get_msg(&self) -> &str { self.msg.as_ref() }

    pub fn render(&mut self) -> Result<()> {

        //let (screen_w, screen_h) = term_utils::get_winsize().unwrap();

        if self.marker_position <= 0 {
            self.marker_position = 0;
            self.step = 1;
        } else if self.marker_position > 9 {
            self.marker_position = 10;
            self.step = -1;
        }
        self.marker_position = self.marker_position + self.step;

        let mut bar:String = "..........".to_owned(); //10 dots
        bar.insert(self.marker_position as usize, '#');

        try!(self.write(format!("\r{msg} [{bar}]", msg=self.msg, bar=bar)));
        Ok(())

    }

}
<|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/. */

use euclid::{TypedPoint2D, TypedVector2D};
use glutin_app::keyutils::{CMD_OR_CONTROL, CMD_OR_ALT};
use glutin_app::window::{Window, LINE_HEIGHT};
use servo::compositing::compositor_thread::EmbedderMsg;
use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent};
use servo::ipc_channel::ipc::IpcSender;
use servo::msg::constellation_msg::{Key, TopLevelBrowsingContextId as BrowserId};
use servo::msg::constellation_msg::{KeyModifiers, KeyState, TraversalDirection};
use servo::net_traits::pub_domains::is_reg_domain;
use servo::script_traits::TouchEventType;
use servo::servo_config::prefs::PREFS;
use servo::servo_url::ServoUrl;
use servo::webrender_api::ScrollLocation;
use std::mem;
use std::rc::Rc;
#[cfg(target_os = "linux")]
use std::thread;
use tinyfiledialogs;

pub struct Browser {
    current_url: Option<ServoUrl>,
    /// id of the top level browsing context. It is unique as tabs
    /// are not supported yet. None until created.
    browser_id: Option<BrowserId>,

    title: Option<String>,
    status: Option<String>,
    favicon: Option<ServoUrl>,
    loading_state: Option<LoadingState>,
    window: Rc<Window>,
    event_queue: Vec<WindowEvent>,
    shutdown_requested: bool,
}

enum LoadingState {
    Connecting,
    Loading,
    Loaded,
}

impl Browser {
    pub fn new(window: Rc<Window>) -> Browser {
        Browser {
            title: None,
            current_url: None,
            browser_id: None,
            status: None,
            favicon: None,
            loading_state: None,
            window: window,
            event_queue: Vec::new(),
            shutdown_requested: false,
        }
    }

    pub fn get_events(&mut self) -> Vec<WindowEvent> {
        mem::replace(&mut self.event_queue, Vec::new())
    }

    pub fn set_browser_id(&mut self, browser_id: BrowserId) {
        self.browser_id = Some(browser_id);
    }

    pub fn handle_window_events(&mut self, events: Vec<WindowEvent>) {
        for event in events {
            match event {
                WindowEvent::KeyEvent(ch, key, state, mods) => {
                    self.handle_key_from_window(ch, key, state, mods);
                },
                event => {
                    self.event_queue.push(event);
                }
            }
        }
    }

    pub fn shutdown_requested(&self) -> bool {
        self.shutdown_requested
    }

    /// Handle key events before sending them to Servo.
    fn handle_key_from_window(&mut self, ch: Option<char>, key: Key, state: KeyState, mods: KeyModifiers) {
        match (mods, ch, key) {
            (CMD_OR_CONTROL, Some('r'), _) => {
                if let Some(id) = self.browser_id {
                    self.event_queue.push(WindowEvent::Reload(id));
                }
            }
            (CMD_OR_CONTROL, Some('l'), _) => {
                if let Some(id) = self.browser_id {
                    let url: String = if let Some(ref current_url) = self.current_url {
                        current_url.to_string()
                    } else {
                        String::from("")
                    };
                    let title = "URL or search query";
                    if let Some(input) = tinyfiledialogs::input_box(title, title, &url) {
                        if let Some(url) = sanitize_url(&input) {
                            self.event_queue.push(WindowEvent::LoadUrl(id, url));
                        }
                    }
                }
            }
            (CMD_OR_CONTROL, Some('q'), _) => {
                self.event_queue.push(WindowEvent::Quit);
            }
            (_, Some('3'), _) => if mods ^ KeyModifiers::CONTROL == KeyModifiers::SHIFT {
                self.event_queue.push(WindowEvent::CaptureWebRender);
            }
            (KeyModifiers::CONTROL, None, Key::F10) => {
                let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::RenderTargetDebug);
                self.event_queue.push(event);
            }
            (KeyModifiers::CONTROL, None, Key::F11) => {
                let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::TextureCacheDebug);
                self.event_queue.push(event);
            }
            (KeyModifiers::CONTROL, None, Key::F12) => {
                let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::Profiler);
                self.event_queue.push(event);
            }
            (CMD_OR_ALT, None, Key::Right) | (KeyModifiers::NONE, None, Key::NavigateForward) => {
                if let Some(id) = self.browser_id {
                    let event = WindowEvent::Navigation(id, TraversalDirection::Forward(1));
                    self.event_queue.push(event);
                }
            }
            (CMD_OR_ALT, None, Key::Left) | (KeyModifiers::NONE, None, Key::NavigateBackward) => {
                if let Some(id) = self.browser_id {
                    let event = WindowEvent::Navigation(id, TraversalDirection::Back(1));
                    self.event_queue.push(event);
                }
            }
            (KeyModifiers::NONE, None, Key::Escape) => {
                self.event_queue.push(WindowEvent::Quit);
            }
            _ => {
                let event = self.platform_handle_key(key, mods);
                self.event_queue.push(event.unwrap_or(WindowEvent::KeyEvent(ch, key, state, mods)));
            }
        }

    }

    #[cfg(not(target_os = "win"))]
    fn platform_handle_key(&self, key: Key, mods: KeyModifiers) -> Option<WindowEvent> {
        match (mods, key, self.browser_id) {
            (CMD_OR_CONTROL, Key::LeftBracket, Some(id)) => {
                Some(WindowEvent::Navigation(id, TraversalDirection::Back(1)))
            }
            (CMD_OR_CONTROL, Key::RightBracket, Some(id)) => {
                Some(WindowEvent::Navigation(id, TraversalDirection::Forward(1)))
            }
            _ => None
        }
    }

    #[cfg(target_os = "win")]
    fn platform_handle_key(&self, key: Key, mods: KeyModifiers) -> Option<WindowEvent> {
        None
    }

    /// Handle key events after they have been handled by Servo.
    fn handle_key_from_servo(&mut self, _: Option<BrowserId>, ch: Option<char>,
                             key: Key, _: KeyState, mods: KeyModifiers) {
        match (mods, ch, key) {
            (_, Some('+'), _) => {
                if mods & !KeyModifiers::SHIFT == CMD_OR_CONTROL {
                    self.event_queue.push(WindowEvent::Zoom(1.1));
                } else if mods & !KeyModifiers::SHIFT == CMD_OR_CONTROL | KeyModifiers::ALT {
                    self.event_queue.push(WindowEvent::PinchZoom(1.1));
                }
            }
            (CMD_OR_CONTROL, Some('-'), _) => {
                self.event_queue.push(WindowEvent::Zoom(1.0 / 1.1));
            }
            (_, Some('-'), _) if mods == CMD_OR_CONTROL | KeyModifiers::ALT => {
                self.event_queue.push(WindowEvent::PinchZoom(1.0 / 1.1));
            }
            (CMD_OR_CONTROL, Some('0'), _) => {
                self.event_queue.push(WindowEvent::ResetZoom);
            }

            (KeyModifiers::NONE, None, Key::PageDown) => {
               let scroll_location = ScrollLocation::Delta(TypedVector2D::new(0.0,
                                   -self.window.page_height() + 2.0 * LINE_HEIGHT));
                self.scroll_window_from_key(scroll_location, TouchEventType::Move);
            }
            (KeyModifiers::NONE, None, Key::PageUp) => {
                let scroll_location = ScrollLocation::Delta(TypedVector2D::new(0.0,
                                   self.window.page_height() - 2.0 * LINE_HEIGHT));
                self.scroll_window_from_key(scroll_location, TouchEventType::Move);
            }

            (KeyModifiers::NONE, None, Key::Home) => {
                self.scroll_window_from_key(ScrollLocation::Start, TouchEventType::Move);
            }

            (KeyModifiers::NONE, None, Key::End) => {
                self.scroll_window_from_key(ScrollLocation::End, TouchEventType::Move);
            }

            (KeyModifiers::NONE, None, Key::Up) => {
                self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(0.0, 3.0 * LINE_HEIGHT)),
                                            TouchEventType::Move);
            }
            (KeyModifiers::NONE, None, Key::Down) => {
                self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(0.0, -3.0 * LINE_HEIGHT)),
                                            TouchEventType::Move);
            }
            (KeyModifiers::NONE, None, Key::Left) => {
                self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(LINE_HEIGHT, 0.0)),
                                            TouchEventType::Move);
            }
            (KeyModifiers::NONE, None, Key::Right) => {
                self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(-LINE_HEIGHT, 0.0)),
                                            TouchEventType::Move);
            }

            _ => {
            }
        }
    }

    fn scroll_window_from_key(&mut self, scroll_location: ScrollLocation, phase: TouchEventType) {
        let event = WindowEvent::Scroll(scroll_location, TypedPoint2D::zero(), phase);
        self.event_queue.push(event);
    }

    pub fn handle_servo_events(&mut self, events: Vec<EmbedderMsg>) {
        for event in events {
            match event {
                EmbedderMsg::Status(_browser_id, status) => {
                    self.status = status;
                },
                EmbedderMsg::ChangePageTitle(_browser_id, title) => {
                    self.title = title;

                    let fallback_title: String = if let Some(ref current_url) = self.current_url {
                        current_url.to_string()
                    } else {
                        String::from("Untitled")
                    };
                    let title = match self.title {
                        Some(ref title) if title.len() > 0 => &**title,
                        _ => &fallback_title,
                    };
                    let title = format!("{} - Servo", title);
                    self.window.set_title(&title);
                }
                EmbedderMsg::MoveTo(_browser_id, point) => {
                    self.window.set_position(point);
                }
                EmbedderMsg::ResizeTo(_browser_id, size) => {
                    self.window.set_inner_size(size);
                }
                EmbedderMsg::AllowNavigation(_browser_id, _url, response_chan) => {
                    if let Err(e) = response_chan.send(true) {
                        warn!("Failed to send allow_navigation() response: {}", e);
                    };
                }
                EmbedderMsg::KeyEvent(browser_id, ch, key, state, modified) => {
                    self.handle_key_from_servo(browser_id, ch, key, state, modified);
                }
                EmbedderMsg::SetCursor(cursor) => {
                    self.window.set_cursor(cursor);
                }
                EmbedderMsg::NewFavicon(_browser_id, url) => {
                    self.favicon = Some(url);
                }
                EmbedderMsg::HeadParsed(_browser_id, ) => {
                    self.loading_state = Some(LoadingState::Loading);
                }
                EmbedderMsg::HistoryChanged(_browser_id, entries, current) => {
                    self.current_url = Some(entries[current].url.clone());
                }
                EmbedderMsg::SetFullscreenState(_browser_id, state) => {
                    self.window.set_fullscreen(state);
                }
                EmbedderMsg::LoadStart(_browser_id) => {
                    self.loading_state = Some(LoadingState::Connecting);
                }
                EmbedderMsg::LoadComplete(_browser_id) => {
                    self.loading_state = Some(LoadingState::Loaded);
                }
                EmbedderMsg::Shutdown => {
                    self.shutdown_requested = true;
                },
                EmbedderMsg::Panic(_browser_id, _reason, _backtrace) => {
                },
                EmbedderMsg::GetSelectedBluetoothDevice(devices, sender) => {
                    platform_get_selected_devices(devices, sender);
                }
                EmbedderMsg::ShowIME(_browser_id, _kind) => {
                    debug!("ShowIME received");
                }
                EmbedderMsg::HideIME(_browser_id) => {
                    debug!("HideIME received");
                }
            }
        }
    }

}

#[cfg(target_os = "linux")]
fn platform_get_selected_devices(devices: Vec<String>, sender: IpcSender<Option<String>>) {
    let picker_name = "Choose a device";

    thread::Builder::new().name(picker_name.to_owned()).spawn(move || {
        let dialog_rows: Vec<&str> = devices.iter()
            .map(|s| s.as_ref())
            .collect();
        let dialog_rows: Option<&[&str]> = Some(dialog_rows.as_slice());

        match tinyfiledialogs::list_dialog("Choose a device", &["Id", "Name"], dialog_rows) {
            Some(device) => {
                // The device string format will be "Address|Name". We need the first part of it.
                let address = device.split("|").next().map(|s| s.to_string());
                let _ = sender.send(address);
            },
            None => {
                let _ = sender.send(None);
            }
        }
    }).expect("Thread spawning failed");
}

#[cfg(not(target_os = "linux"))]
fn platform_get_selected_devices(devices: Vec<String>, sender: IpcSender<Option<String>>) {
    for device in devices {
        if let Some(address) = device.split("|").next().map(|s| s.to_string()) {
            let _ = sender.send(Some(address));
        }
    }
    let _ = sender.send(None);
}

fn sanitize_url(request: &str) -> Option<ServoUrl> {
    let request = request.trim();
    ServoUrl::parse(&request).ok()
        .or_else(|| {
            if request.contains('/') || is_reg_domain(request) {
                ServoUrl::parse(&format!("http://{}", request)).ok()
            } else {
                None
            }
        }).or_else(|| {
            PREFS.get("shell.searchpage").as_string().and_then(|s: &str| {
                let url = s.replace("%s", request);
                ServoUrl::parse(&url).ok()
            })
        })
}
<commit_msg>Auto merge of #20687 - kwonoj:fix-capture-wr-guard, r=KiChjang<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/. */

use euclid::{TypedPoint2D, TypedVector2D};
use glutin_app::keyutils::{CMD_OR_CONTROL, CMD_OR_ALT};
use glutin_app::window::{Window, LINE_HEIGHT};
use servo::compositing::compositor_thread::EmbedderMsg;
use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent};
use servo::ipc_channel::ipc::IpcSender;
use servo::msg::constellation_msg::{Key, TopLevelBrowsingContextId as BrowserId};
use servo::msg::constellation_msg::{KeyModifiers, KeyState, TraversalDirection};
use servo::net_traits::pub_domains::is_reg_domain;
use servo::script_traits::TouchEventType;
use servo::servo_config::prefs::PREFS;
use servo::servo_url::ServoUrl;
use servo::webrender_api::ScrollLocation;
use std::mem;
use std::rc::Rc;
#[cfg(target_os = "linux")]
use std::thread;
use tinyfiledialogs;

pub struct Browser {
    current_url: Option<ServoUrl>,
    /// id of the top level browsing context. It is unique as tabs
    /// are not supported yet. None until created.
    browser_id: Option<BrowserId>,

    title: Option<String>,
    status: Option<String>,
    favicon: Option<ServoUrl>,
    loading_state: Option<LoadingState>,
    window: Rc<Window>,
    event_queue: Vec<WindowEvent>,
    shutdown_requested: bool,
}

enum LoadingState {
    Connecting,
    Loading,
    Loaded,
}

impl Browser {
    pub fn new(window: Rc<Window>) -> Browser {
        Browser {
            title: None,
            current_url: None,
            browser_id: None,
            status: None,
            favicon: None,
            loading_state: None,
            window: window,
            event_queue: Vec::new(),
            shutdown_requested: false,
        }
    }

    pub fn get_events(&mut self) -> Vec<WindowEvent> {
        mem::replace(&mut self.event_queue, Vec::new())
    }

    pub fn set_browser_id(&mut self, browser_id: BrowserId) {
        self.browser_id = Some(browser_id);
    }

    pub fn handle_window_events(&mut self, events: Vec<WindowEvent>) {
        for event in events {
            match event {
                WindowEvent::KeyEvent(ch, key, state, mods) => {
                    self.handle_key_from_window(ch, key, state, mods);
                },
                event => {
                    self.event_queue.push(event);
                }
            }
        }
    }

    pub fn shutdown_requested(&self) -> bool {
        self.shutdown_requested
    }

    /// Handle key events before sending them to Servo.
    fn handle_key_from_window(&mut self, ch: Option<char>, key: Key, state: KeyState, mods: KeyModifiers) {
        match (mods, ch, key) {
            (CMD_OR_CONTROL, Some('r'), _) => {
                if let Some(id) = self.browser_id {
                    self.event_queue.push(WindowEvent::Reload(id));
                }
            }
            (CMD_OR_CONTROL, Some('l'), _) => {
                if let Some(id) = self.browser_id {
                    let url: String = if let Some(ref current_url) = self.current_url {
                        current_url.to_string()
                    } else {
                        String::from("")
                    };
                    let title = "URL or search query";
                    if let Some(input) = tinyfiledialogs::input_box(title, title, &url) {
                        if let Some(url) = sanitize_url(&input) {
                            self.event_queue.push(WindowEvent::LoadUrl(id, url));
                        }
                    }
                }
            }
            (CMD_OR_CONTROL, Some('q'), _) => {
                self.event_queue.push(WindowEvent::Quit);
            }
            (_, Some('3'), _) if mods ^ KeyModifiers::CONTROL == KeyModifiers::SHIFT => {
                self.event_queue.push(WindowEvent::CaptureWebRender);
            }
            (KeyModifiers::CONTROL, None, Key::F10) => {
                let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::RenderTargetDebug);
                self.event_queue.push(event);
            }
            (KeyModifiers::CONTROL, None, Key::F11) => {
                let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::TextureCacheDebug);
                self.event_queue.push(event);
            }
            (KeyModifiers::CONTROL, None, Key::F12) => {
                let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::Profiler);
                self.event_queue.push(event);
            }
            (CMD_OR_ALT, None, Key::Right) | (KeyModifiers::NONE, None, Key::NavigateForward) => {
                if let Some(id) = self.browser_id {
                    let event = WindowEvent::Navigation(id, TraversalDirection::Forward(1));
                    self.event_queue.push(event);
                }
            }
            (CMD_OR_ALT, None, Key::Left) | (KeyModifiers::NONE, None, Key::NavigateBackward) => {
                if let Some(id) = self.browser_id {
                    let event = WindowEvent::Navigation(id, TraversalDirection::Back(1));
                    self.event_queue.push(event);
                }
            }
            (KeyModifiers::NONE, None, Key::Escape) => {
                self.event_queue.push(WindowEvent::Quit);
            }
            _ => {
                let event = self.platform_handle_key(key, mods);
                self.event_queue.push(event.unwrap_or(WindowEvent::KeyEvent(ch, key, state, mods)));
            }
        }

    }

    #[cfg(not(target_os = "win"))]
    fn platform_handle_key(&self, key: Key, mods: KeyModifiers) -> Option<WindowEvent> {
        match (mods, key, self.browser_id) {
            (CMD_OR_CONTROL, Key::LeftBracket, Some(id)) => {
                Some(WindowEvent::Navigation(id, TraversalDirection::Back(1)))
            }
            (CMD_OR_CONTROL, Key::RightBracket, Some(id)) => {
                Some(WindowEvent::Navigation(id, TraversalDirection::Forward(1)))
            }
            _ => None
        }
    }

    #[cfg(target_os = "win")]
    fn platform_handle_key(&self, key: Key, mods: KeyModifiers) -> Option<WindowEvent> {
        None
    }

    /// Handle key events after they have been handled by Servo.
    fn handle_key_from_servo(&mut self, _: Option<BrowserId>, ch: Option<char>,
                             key: Key, _: KeyState, mods: KeyModifiers) {
        match (mods, ch, key) {
            (_, Some('+'), _) => {
                if mods & !KeyModifiers::SHIFT == CMD_OR_CONTROL {
                    self.event_queue.push(WindowEvent::Zoom(1.1));
                } else if mods & !KeyModifiers::SHIFT == CMD_OR_CONTROL | KeyModifiers::ALT {
                    self.event_queue.push(WindowEvent::PinchZoom(1.1));
                }
            }
            (CMD_OR_CONTROL, Some('-'), _) => {
                self.event_queue.push(WindowEvent::Zoom(1.0 / 1.1));
            }
            (_, Some('-'), _) if mods == CMD_OR_CONTROL | KeyModifiers::ALT => {
                self.event_queue.push(WindowEvent::PinchZoom(1.0 / 1.1));
            }
            (CMD_OR_CONTROL, Some('0'), _) => {
                self.event_queue.push(WindowEvent::ResetZoom);
            }

            (KeyModifiers::NONE, None, Key::PageDown) => {
               let scroll_location = ScrollLocation::Delta(TypedVector2D::new(0.0,
                                   -self.window.page_height() + 2.0 * LINE_HEIGHT));
                self.scroll_window_from_key(scroll_location, TouchEventType::Move);
            }
            (KeyModifiers::NONE, None, Key::PageUp) => {
                let scroll_location = ScrollLocation::Delta(TypedVector2D::new(0.0,
                                   self.window.page_height() - 2.0 * LINE_HEIGHT));
                self.scroll_window_from_key(scroll_location, TouchEventType::Move);
            }

            (KeyModifiers::NONE, None, Key::Home) => {
                self.scroll_window_from_key(ScrollLocation::Start, TouchEventType::Move);
            }

            (KeyModifiers::NONE, None, Key::End) => {
                self.scroll_window_from_key(ScrollLocation::End, TouchEventType::Move);
            }

            (KeyModifiers::NONE, None, Key::Up) => {
                self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(0.0, 3.0 * LINE_HEIGHT)),
                                            TouchEventType::Move);
            }
            (KeyModifiers::NONE, None, Key::Down) => {
                self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(0.0, -3.0 * LINE_HEIGHT)),
                                            TouchEventType::Move);
            }
            (KeyModifiers::NONE, None, Key::Left) => {
                self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(LINE_HEIGHT, 0.0)),
                                            TouchEventType::Move);
            }
            (KeyModifiers::NONE, None, Key::Right) => {
                self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(-LINE_HEIGHT, 0.0)),
                                            TouchEventType::Move);
            }

            _ => {
            }
        }
    }

    fn scroll_window_from_key(&mut self, scroll_location: ScrollLocation, phase: TouchEventType) {
        let event = WindowEvent::Scroll(scroll_location, TypedPoint2D::zero(), phase);
        self.event_queue.push(event);
    }

    pub fn handle_servo_events(&mut self, events: Vec<EmbedderMsg>) {
        for event in events {
            match event {
                EmbedderMsg::Status(_browser_id, status) => {
                    self.status = status;
                },
                EmbedderMsg::ChangePageTitle(_browser_id, title) => {
                    self.title = title;

                    let fallback_title: String = if let Some(ref current_url) = self.current_url {
                        current_url.to_string()
                    } else {
                        String::from("Untitled")
                    };
                    let title = match self.title {
                        Some(ref title) if title.len() > 0 => &**title,
                        _ => &fallback_title,
                    };
                    let title = format!("{} - Servo", title);
                    self.window.set_title(&title);
                }
                EmbedderMsg::MoveTo(_browser_id, point) => {
                    self.window.set_position(point);
                }
                EmbedderMsg::ResizeTo(_browser_id, size) => {
                    self.window.set_inner_size(size);
                }
                EmbedderMsg::AllowNavigation(_browser_id, _url, response_chan) => {
                    if let Err(e) = response_chan.send(true) {
                        warn!("Failed to send allow_navigation() response: {}", e);
                    };
                }
                EmbedderMsg::KeyEvent(browser_id, ch, key, state, modified) => {
                    self.handle_key_from_servo(browser_id, ch, key, state, modified);
                }
                EmbedderMsg::SetCursor(cursor) => {
                    self.window.set_cursor(cursor);
                }
                EmbedderMsg::NewFavicon(_browser_id, url) => {
                    self.favicon = Some(url);
                }
                EmbedderMsg::HeadParsed(_browser_id, ) => {
                    self.loading_state = Some(LoadingState::Loading);
                }
                EmbedderMsg::HistoryChanged(_browser_id, entries, current) => {
                    self.current_url = Some(entries[current].url.clone());
                }
                EmbedderMsg::SetFullscreenState(_browser_id, state) => {
                    self.window.set_fullscreen(state);
                }
                EmbedderMsg::LoadStart(_browser_id) => {
                    self.loading_state = Some(LoadingState::Connecting);
                }
                EmbedderMsg::LoadComplete(_browser_id) => {
                    self.loading_state = Some(LoadingState::Loaded);
                }
                EmbedderMsg::Shutdown => {
                    self.shutdown_requested = true;
                },
                EmbedderMsg::Panic(_browser_id, _reason, _backtrace) => {
                },
                EmbedderMsg::GetSelectedBluetoothDevice(devices, sender) => {
                    platform_get_selected_devices(devices, sender);
                }
                EmbedderMsg::ShowIME(_browser_id, _kind) => {
                    debug!("ShowIME received");
                }
                EmbedderMsg::HideIME(_browser_id) => {
                    debug!("HideIME received");
                }
            }
        }
    }

}

#[cfg(target_os = "linux")]
fn platform_get_selected_devices(devices: Vec<String>, sender: IpcSender<Option<String>>) {
    let picker_name = "Choose a device";

    thread::Builder::new().name(picker_name.to_owned()).spawn(move || {
        let dialog_rows: Vec<&str> = devices.iter()
            .map(|s| s.as_ref())
            .collect();
        let dialog_rows: Option<&[&str]> = Some(dialog_rows.as_slice());

        match tinyfiledialogs::list_dialog("Choose a device", &["Id", "Name"], dialog_rows) {
            Some(device) => {
                // The device string format will be "Address|Name". We need the first part of it.
                let address = device.split("|").next().map(|s| s.to_string());
                let _ = sender.send(address);
            },
            None => {
                let _ = sender.send(None);
            }
        }
    }).expect("Thread spawning failed");
}

#[cfg(not(target_os = "linux"))]
fn platform_get_selected_devices(devices: Vec<String>, sender: IpcSender<Option<String>>) {
    for device in devices {
        if let Some(address) = device.split("|").next().map(|s| s.to_string()) {
            let _ = sender.send(Some(address));
        }
    }
    let _ = sender.send(None);
}

fn sanitize_url(request: &str) -> Option<ServoUrl> {
    let request = request.trim();
    ServoUrl::parse(&request).ok()
        .or_else(|| {
            if request.contains('/') || is_reg_domain(request) {
                ServoUrl::parse(&format!("http://{}", request)).ok()
            } else {
                None
            }
        }).or_else(|| {
            PREFS.get("shell.searchpage").as_string().and_then(|s: &str| {
                let url = s.replace("%s", request);
                ServoUrl::parse(&url).ok()
            })
        })
}
<|endoftext|>