<commit_before>extern crate unicode_names;

use std::marker::PhantomData;

use irc::client::prelude::*;

use plugin::*;
use FrippyClient;

use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::Fail;

#[derive(PluginName, Default, Debug)]
pub struct Unicode<C> {
    phantom: PhantomData<C>,
}

impl<C: FrippyClient> Unicode<C> {
    pub fn new() -> Unicode<C> {
        Unicode {
            phantom: PhantomData,
        }
    }

    fn get_name(&self, symbol: char) -> String {
        match unicode_names::name(symbol) {
            Some(sym) => sym.to_string().to_lowercase(),
            None => String::from("UNKNOWN"),
        }
    }

    fn format_response(&self, content: &str) -> String {
        let character = content
            .chars()
            .next()
            .expect("content contains at least one character");

        let mut buf = [0; 4];

        let byte_string = character
            .encode_utf8(&mut buf)
            .as_bytes()
            .iter()
            .map(|b| format!("{:#x}", b))
            .collect::<Vec<String>>()
            .join(",");

        let name = self.get_name(character);

        format!(
            "{} is '{}' | UTF-8: {2:#x} ({2}), Bytes: [{3}]",
            character, name, character as u32, byte_string
        )
    }
}

impl<C: FrippyClient> Plugin for Unicode<C> {
    type Client = C;

    fn execute(&self, _: &Self::Client, _: &Message) -> ExecutionStatus {
        ExecutionStatus::Done
    }

    fn execute_threaded(&self, _: &Self::Client, _: &Message) -> Result<(), FrippyError> {
        panic!("Unicode should not use threading")
    }

    fn command(&self, client: &Self::Client, command: PluginCommand) -> Result<(), FrippyError> {
        let token = match command.tokens.iter().find(|t| !t.is_empty()) {
            Some(t) => t,
            None => {
                let msg = "No non-space character was found.";

                if let Err(e) = client.send_notice(command.source, msg) {
                    Err(e.context(FrippyErrorKind::Connection))?;
                }

                return Ok(());
            }
        };

        if let Err(e) = client.send_privmsg(command.target, &self.format_response(&token)) {
            Err(e.context(FrippyErrorKind::Connection))?;
        }

        Ok(())
    }

    fn evaluate(&self, _: &Self::Client, command: PluginCommand) -> Result<String, String> {
        let tokens = command.tokens;

        if tokens.is_empty() {
            return Err(String::from("No non-space character was found."));
        }

        Ok(self.format_response(&tokens[0]))
    }
}
<commit_msg>Unicode: Hide bytes if there is only one<commit_after>extern crate unicode_names;

use std::marker::PhantomData;

use irc::client::prelude::*;

use plugin::*;
use FrippyClient;

use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::Fail;

#[derive(PluginName, Default, Debug)]
pub struct Unicode<C> {
    phantom: PhantomData<C>,
}

impl<C: FrippyClient> Unicode<C> {
    pub fn new() -> Unicode<C> {
        Unicode {
            phantom: PhantomData,
        }
    }

    fn get_name(&self, symbol: char) -> String {
        match unicode_names::name(symbol) {
            Some(sym) => sym.to_string().to_lowercase(),
            None => String::from("UNKNOWN"),
        }
    }

    fn format_response(&self, content: &str) -> String {
        let character = content
            .chars()
            .next()
            .expect("content contains at least one character");

        let mut buf = [0; 4];

        let bytes = character
            .encode_utf8(&mut buf)
            .as_bytes()
            .iter()
            .map(|b| format!("{:#x}", b))
            .collect::<Vec<String>>();

        let name = self.get_name(character);

        if bytes.len() > 1 {
            format!(
                "{} is '{}' | UTF-8: {2:#x} ({2}), Bytes: [{3}]",
                character, name, character as u32, bytes.join(",")
            )
        } else {
            format!(
                "{} is '{}' | UTF-8: {2:#x} ({2})",
                character, name, character as u32
            )
        }
    }
}

impl<C: FrippyClient> Plugin for Unicode<C> {
    type Client = C;

    fn execute(&self, _: &Self::Client, _: &Message) -> ExecutionStatus {
        ExecutionStatus::Done
    }

    fn execute_threaded(&self, _: &Self::Client, _: &Message) -> Result<(), FrippyError> {
        panic!("Unicode should not use threading")
    }

    fn command(&self, client: &Self::Client, command: PluginCommand) -> Result<(), FrippyError> {
        let token = match command.tokens.iter().find(|t| !t.is_empty()) {
            Some(t) => t,
            None => {
                let msg = "No non-space character was found.";

                if let Err(e) = client.send_notice(command.source, msg) {
                    Err(e.context(FrippyErrorKind::Connection))?;
                }

                return Ok(());
            }
        };

        if let Err(e) = client.send_privmsg(command.target, &self.format_response(&token)) {
            Err(e.context(FrippyErrorKind::Connection))?;
        }

        Ok(())
    }

    fn evaluate(&self, _: &Self::Client, command: PluginCommand) -> Result<String, String> {
        let tokens = command.tokens;

        if tokens.is_empty() {
            return Err(String::from("No non-space character was found."));
        }

        Ok(self.format_response(&tokens[0]))
    }
}
<|endoftext|>


<commit_before>//! This module handles auto-magic editing actions applied together with users
//! edits. For example, if the user typed
//!
//! ```text
//!     foo
//!         .bar()
//!         .baz()
//!     |   // <- cursor is here
//! ```
//!
//! and types `.` next, we want to indent the dot.
//!
//! Language server executes such typing assists synchronously. That is, they
//! block user's typing and should be pretty fast for this reason!

mod on_enter;

use ide_db::{
    base_db::{FilePosition, SourceDatabase},
    RootDatabase,
};
use syntax::{
    algo::find_node_at_offset,
    ast::{self, edit::IndentLevel, AstToken},
    AstNode, Parse, SourceFile,
    SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
    TextRange, TextSize,
};

use text_edit::{Indel, TextEdit};

use crate::SourceChange;

pub(crate) use on_enter::on_enter;

// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
pub(crate) const TRIGGER_CHARS: &str = ".=>{";

// Feature: On Typing Assists
//
// Some features trigger on typing certain characters:
//
// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
// - typing `.` in a chain method call auto-indents
// - typing `{` in front of an expression inserts a closing `}` after the expression
//
// VS Code::
//
// Add the following to `settings.json`:
// [source,json]
// ----
// "editor.formatOnType": true,
// ----
//
// image::https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif[]
// image::https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif[]
pub(crate) fn on_char_typed(
    db: &RootDatabase,
    position: FilePosition,
    char_typed: char,
) -> Option<SourceChange> {
    if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
        return None;
    }
    let file = &db.parse(position.file_id);
    if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) {
        return None;
    }
    let edit = on_char_typed_inner(file, position.offset, char_typed)?;
    Some(SourceChange::from_text_edit(position.file_id, edit))
}

fn on_char_typed_inner(
    file: &Parse<SourceFile>,
    offset: TextSize,
    char_typed: char,
) -> Option<TextEdit> {
    if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
        return None;
    }
    match char_typed {
        '.' => on_dot_typed(&file.tree(), offset),
        '=' => on_eq_typed(&file.tree(), offset),
        '>' => on_arrow_typed(&file.tree(), offset),
        '{' => on_opening_brace_typed(file, offset),
        _ => unreachable!(),
    }
}

/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
/// block.
fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
    if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
        return None;
    }

    let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;

    // Remove the `{` to get a better parse tree, and reparse
    let file = file.reparse(&Indel::delete(brace_token.text_range()));

    let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?;
    if expr.syntax().text_range().start() != offset {
        return None;
    }

    // Enclose the outermost expression starting at `offset`
    while let Some(parent) = expr.syntax().parent() {
        if parent.text_range().start() != expr.syntax().text_range().start() {
            break;
        }

        match ast::Expr::cast(parent) {
            Some(parent) => expr = parent,
            None => break,
        }
    }

    // If it's a statement in a block, we don't know how many statements should be included
    if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) {
        return None;
    }

    // Insert `}` right after the expression.
    Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string()))
}

/// Returns an edit which should be applied after `=` was typed. Primarily,
/// this works when adding `let =`.
// FIXME: use a snippet completion instead of this hack here.
fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
    if !stdx::always!(file.syntax().text().char_at(offset) == Some('=')) {
        return None;
    }
    let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
    if let_stmt.semicolon_token().is_some() {
        return None;
    }
    if let Some(expr) = let_stmt.initializer() {
        let expr_range = expr.syntax().text_range();
        if expr_range.contains(offset) && offset != expr_range.start() {
            return None;
        }
        if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
            return None;
        }
    } else {
        return None;
    }
    let offset = let_stmt.syntax().text_range().end();
    Some(TextEdit::insert(offset, ";".to_string()))
}

/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
    if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) {
        return None;
    }
    let whitespace =
        file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;

    let current_indent = {
        let text = whitespace.text();
        let newline = text.rfind('\n')?;
        &text[newline + 1..]
    };
    let current_indent_len = TextSize::of(current_indent);

    let parent = whitespace.syntax().parent()?;
    // Make sure dot is a part of call chain
    if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
        return None;
    }
    let prev_indent = IndentLevel::from_node(&parent);
    let target_indent = format!("    {}", prev_indent);
    let target_indent_len = TextSize::of(&target_indent);
    if current_indent_len == target_indent_len {
        return None;
    }

    Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent))
}

/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
    let file_text = file.syntax().text();
    if !stdx::always!(file_text.char_at(offset) == Some('>')) {
        return None;
    }
    let after_arrow = offset + TextSize::of('>');
    if file_text.char_at(after_arrow) != Some('{') {
        return None;
    }
    if find_node_at_offset::<ast::RetType>(file.syntax(), offset).is_none() {
        return None;
    }

    Some(TextEdit::insert(after_arrow, " ".to_string()))
}

#[cfg(test)]
mod tests {
    use test_utils::{assert_eq_text, extract_offset};

    use super::*;

    fn do_type_char(char_typed: char, before: &str) -> Option<String> {
        let (offset, mut before) = extract_offset(before);
        let edit = TextEdit::insert(offset, char_typed.to_string());
        edit.apply(&mut before);
        let parse = SourceFile::parse(&before);
        on_char_typed_inner(&parse, offset, char_typed).map(|it| {
            it.apply(&mut before);
            before.to_string()
        })
    }

    fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
        let actual = do_type_char(char_typed, ra_fixture_before)
            .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));

        assert_eq_text!(ra_fixture_after, &actual);
    }

    fn type_char_noop(char_typed: char, before: &str) {
        let file_change = do_type_char(char_typed, before);
        assert!(file_change.is_none())
    }

    #[test]
    fn test_on_eq_typed() {
        //     do_check(r"
        // fn foo() {
        //     let foo =$0
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        // }
        // ");
        type_char(
            '=',
            r"
fn foo() {
    let foo $0 1 + 1
}
",
            r"
fn foo() {
    let foo = 1 + 1;
}
",
        );
        //     do_check(r"
        // fn foo() {
        //     let foo =$0
        //     let bar = 1;
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        //     let bar = 1;
        // }
        // ");
    }

    #[test]
    fn indents_new_chain_call() {
        type_char(
            '.',
            r"
            fn main() {
                xs.foo()
                $0
            }
            ",
            r"
            fn main() {
                xs.foo()
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn main() {
                xs.foo()
                    $0
            }
            ",
        )
    }

    #[test]
    fn indents_new_chain_call_with_semi() {
        type_char(
            '.',
            r"
            fn main() {
                xs.foo()
                $0;
            }
            ",
            r"
            fn main() {
                xs.foo()
                    .;
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn main() {
                xs.foo()
                    $0;
            }
            ",
        )
    }

    #[test]
    fn indents_new_chain_call_with_let() {
        type_char(
            '.',
            r#"
fn main() {
    let _ = foo
    $0
    bar()
}
"#,
            r#"
fn main() {
    let _ = foo
        .
    bar()
}
"#,
        );
    }

    #[test]
    fn indents_continued_chain_call() {
        type_char(
            '.',
            r"
            fn main() {
                xs.foo()
                    .first()
                $0
            }
            ",
            r"
            fn main() {
                xs.foo()
                    .first()
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn main() {
                xs.foo()
                    .first()
                    $0
            }
            ",
        );
    }

    #[test]
    fn indents_middle_of_chain_call() {
        type_char(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                $0
                    .nth(92)
                    .unwrap();
            }
            ",
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    .
                    .nth(92)
                    .unwrap();
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    $0
                    .nth(92)
                    .unwrap();
            }
            ",
        );
    }

    #[test]
    fn dont_indent_freestanding_dot() {
        type_char_noop(
            '.',
            r"
            fn main() {
                $0
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn main() {
            $0
            }
            ",
        );
    }

    #[test]
    fn adds_space_after_return_type() {
        type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }")
    }

    #[test]
    fn adds_closing_brace() {
        type_char(
            '{',
            r#"
fn f() { match () { _ => $0() } }
            "#,
            r#"
fn f() { match () { _ => {()} } }
            "#,
        );
        type_char(
            '{',
            r#"
fn f() { $0() }
            "#,
            r#"
fn f() { {()} }
            "#,
        );
        type_char(
            '{',
            r#"
fn f() { let x = $0(); }
            "#,
            r#"
fn f() { let x = {()}; }
            "#,
        );
        type_char(
            '{',
            r#"
fn f() { let x = $0a.b(); }
            "#,
            r#"
fn f() { let x = {a.b()}; }
            "#,
        );
        type_char(
            '{',
            r#"
const S: () = $0();
fn f() {}
            "#,
            r#"
const S: () = {()};
fn f() {}
            "#,
        );
        type_char(
            '{',
            r#"
const S: () = $0a.b();
fn f() {}
            "#,
            r#"
const S: () = {a.b()};
fn f() {}
            "#,
        );
        type_char(
            '{',
            r#"
fn f() {
    match x {
        0 => $0(),
        1 => (),
    }
}
            "#,
            r#"
fn f() {
    match x {
        0 => {()},
        1 => (),
    }
}
            "#,
        );
    }
}
<commit_msg>internal: follow test style guide in typing.rs<commit_after>//! This module handles auto-magic editing actions applied together with users
//! edits. For example, if the user typed
//!
//! ```text
//!     foo
//!         .bar()
//!         .baz()
//!     |   // <- cursor is here
//! ```
//!
//! and types `.` next, we want to indent the dot.
//!
//! Language server executes such typing assists synchronously. That is, they
//! block user's typing and should be pretty fast for this reason!

mod on_enter;

use ide_db::{
    base_db::{FilePosition, SourceDatabase},
    RootDatabase,
};
use syntax::{
    algo::find_node_at_offset,
    ast::{self, edit::IndentLevel, AstToken},
    AstNode, Parse, SourceFile,
    SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
    TextRange, TextSize,
};

use text_edit::{Indel, TextEdit};

use crate::SourceChange;

pub(crate) use on_enter::on_enter;

// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
pub(crate) const TRIGGER_CHARS: &str = ".=>{";

// Feature: On Typing Assists
//
// Some features trigger on typing certain characters:
//
// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
// - typing `.` in a chain method call auto-indents
// - typing `{` in front of an expression inserts a closing `}` after the expression
//
// VS Code::
//
// Add the following to `settings.json`:
// [source,json]
// ----
// "editor.formatOnType": true,
// ----
//
// image::https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif[]
// image::https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif[]
pub(crate) fn on_char_typed(
    db: &RootDatabase,
    position: FilePosition,
    char_typed: char,
) -> Option<SourceChange> {
    if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
        return None;
    }
    let file = &db.parse(position.file_id);
    if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) {
        return None;
    }
    let edit = on_char_typed_inner(file, position.offset, char_typed)?;
    Some(SourceChange::from_text_edit(position.file_id, edit))
}

fn on_char_typed_inner(
    file: &Parse<SourceFile>,
    offset: TextSize,
    char_typed: char,
) -> Option<TextEdit> {
    if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
        return None;
    }
    match char_typed {
        '.' => on_dot_typed(&file.tree(), offset),
        '=' => on_eq_typed(&file.tree(), offset),
        '>' => on_arrow_typed(&file.tree(), offset),
        '{' => on_opening_brace_typed(file, offset),
        _ => unreachable!(),
    }
}

/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
/// block.
fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
    if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
        return None;
    }

    let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;

    // Remove the `{` to get a better parse tree, and reparse
    let file = file.reparse(&Indel::delete(brace_token.text_range()));

    let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?;
    if expr.syntax().text_range().start() != offset {
        return None;
    }

    // Enclose the outermost expression starting at `offset`
    while let Some(parent) = expr.syntax().parent() {
        if parent.text_range().start() != expr.syntax().text_range().start() {
            break;
        }

        match ast::Expr::cast(parent) {
            Some(parent) => expr = parent,
            None => break,
        }
    }

    // If it's a statement in a block, we don't know how many statements should be included
    if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) {
        return None;
    }

    // Insert `}` right after the expression.
    Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string()))
}

/// Returns an edit which should be applied after `=` was typed. Primarily,
/// this works when adding `let =`.
// FIXME: use a snippet completion instead of this hack here.
fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
    if !stdx::always!(file.syntax().text().char_at(offset) == Some('=')) {
        return None;
    }
    let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
    if let_stmt.semicolon_token().is_some() {
        return None;
    }
    if let Some(expr) = let_stmt.initializer() {
        let expr_range = expr.syntax().text_range();
        if expr_range.contains(offset) && offset != expr_range.start() {
            return None;
        }
        if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
            return None;
        }
    } else {
        return None;
    }
    let offset = let_stmt.syntax().text_range().end();
    Some(TextEdit::insert(offset, ";".to_string()))
}

/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
    if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) {
        return None;
    }
    let whitespace =
        file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;

    let current_indent = {
        let text = whitespace.text();
        let newline = text.rfind('\n')?;
        &text[newline + 1..]
    };
    let current_indent_len = TextSize::of(current_indent);

    let parent = whitespace.syntax().parent()?;
    // Make sure dot is a part of call chain
    if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
        return None;
    }
    let prev_indent = IndentLevel::from_node(&parent);
    let target_indent = format!("    {}", prev_indent);
    let target_indent_len = TextSize::of(&target_indent);
    if current_indent_len == target_indent_len {
        return None;
    }

    Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent))
}

/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
    let file_text = file.syntax().text();
    if !stdx::always!(file_text.char_at(offset) == Some('>')) {
        return None;
    }
    let after_arrow = offset + TextSize::of('>');
    if file_text.char_at(after_arrow) != Some('{') {
        return None;
    }
    if find_node_at_offset::<ast::RetType>(file.syntax(), offset).is_none() {
        return None;
    }

    Some(TextEdit::insert(after_arrow, " ".to_string()))
}

#[cfg(test)]
mod tests {
    use test_utils::{assert_eq_text, extract_offset};

    use super::*;

    fn do_type_char(char_typed: char, before: &str) -> Option<String> {
        let (offset, mut before) = extract_offset(before);
        let edit = TextEdit::insert(offset, char_typed.to_string());
        edit.apply(&mut before);
        let parse = SourceFile::parse(&before);
        on_char_typed_inner(&parse, offset, char_typed).map(|it| {
            it.apply(&mut before);
            before.to_string()
        })
    }

    fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
        let actual = do_type_char(char_typed, ra_fixture_before)
            .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));

        assert_eq_text!(ra_fixture_after, &actual);
    }

    fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
        let file_change = do_type_char(char_typed, ra_fixture_before);
        assert!(file_change.is_none())
    }

    #[test]
    fn test_on_eq_typed() {
        //     do_check(r"
        // fn foo() {
        //     let foo =$0
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        // }
        // ");
        type_char(
            '=',
            r#"
fn foo() {
    let foo $0 1 + 1
}
"#,
            r#"
fn foo() {
    let foo = 1 + 1;
}
"#,
        );
        //     do_check(r"
        // fn foo() {
        //     let foo =$0
        //     let bar = 1;
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        //     let bar = 1;
        // }
        // ");
    }

    #[test]
    fn indents_new_chain_call() {
        type_char(
            '.',
            r#"
fn main() {
    xs.foo()
    $0
}
            "#,
            r#"
fn main() {
    xs.foo()
        .
}
            "#,
        );
        type_char_noop(
            '.',
            r#"
fn main() {
    xs.foo()
        $0
}
            "#,
        )
    }

    #[test]
    fn indents_new_chain_call_with_semi() {
        type_char(
            '.',
            r"
fn main() {
    xs.foo()
    $0;
}
            ",
            r#"
fn main() {
    xs.foo()
        .;
}
            "#,
        );
        type_char_noop(
            '.',
            r#"
fn main() {
    xs.foo()
        $0;
}
            "#,
        )
    }

    #[test]
    fn indents_new_chain_call_with_let() {
        type_char(
            '.',
            r#"
fn main() {
    let _ = foo
    $0
    bar()
}
"#,
            r#"
fn main() {
    let _ = foo
        .
    bar()
}
"#,
        );
    }

    #[test]
    fn indents_continued_chain_call() {
        type_char(
            '.',
            r#"
fn main() {
    xs.foo()
        .first()
    $0
}
            "#,
            r#"
fn main() {
    xs.foo()
        .first()
        .
}
            "#,
        );
        type_char_noop(
            '.',
            r#"
fn main() {
    xs.foo()
        .first()
        $0
}
            "#,
        );
    }

    #[test]
    fn indents_middle_of_chain_call() {
        type_char(
            '.',
            r#"
fn source_impl() {
    let var = enum_defvariant_list().unwrap()
    $0
        .nth(92)
        .unwrap();
}
            "#,
            r#"
fn source_impl() {
    let var = enum_defvariant_list().unwrap()
        .
        .nth(92)
        .unwrap();
}
            "#,
        );
        type_char_noop(
            '.',
            r#"
fn source_impl() {
    let var = enum_defvariant_list().unwrap()
        $0
        .nth(92)
        .unwrap();
}
            "#,
        );
    }

    #[test]
    fn dont_indent_freestanding_dot() {
        type_char_noop(
            '.',
            r#"
fn main() {
    $0
}
            "#,
        );
        type_char_noop(
            '.',
            r#"
fn main() {
$0
}
            "#,
        );
    }

    #[test]
    fn adds_space_after_return_type() {
        type_char(
            '>',
            r#"
fn foo() -$0{ 92 }
"#,
            r#"
fn foo() -> { 92 }
"#,
        );
    }

    #[test]
    fn adds_closing_brace() {
        type_char(
            '{',
            r#"
fn f() { match () { _ => $0() } }
            "#,
            r#"
fn f() { match () { _ => {()} } }
            "#,
        );
        type_char(
            '{',
            r#"
fn f() { $0() }
            "#,
            r#"
fn f() { {()} }
            "#,
        );
        type_char(
            '{',
            r#"
fn f() { let x = $0(); }
            "#,
            r#"
fn f() { let x = {()}; }
            "#,
        );
        type_char(
            '{',
            r#"
fn f() { let x = $0a.b(); }
            "#,
            r#"
fn f() { let x = {a.b()}; }
            "#,
        );
        type_char(
            '{',
            r#"
const S: () = $0();
fn f() {}
            "#,
            r#"
const S: () = {()};
fn f() {}
            "#,
        );
        type_char(
            '{',
            r#"
const S: () = $0a.b();
fn f() {}
            "#,
            r#"
const S: () = {a.b()};
fn f() {}
            "#,
        );
        type_char(
            '{',
            r#"
fn f() {
    match x {
        0 => $0(),
        1 => (),
    }
}
            "#,
            r#"
fn f() {
    match x {
        0 => {()},
        1 => (),
    }
}
            "#,
        );
    }
}
<|endoftext|>


<commit_before>mod constants;
mod types;

use super::GbaMemory;
pub use constants::*;
pub use types::*;
use util::bits::Bits as _;

impl GbaMemory {
    pub(super) fn load32_io(&mut self, address: u32) -> u32 {
        let lo = self.load16_io(address) as u32;
        let hi = self.load16_io(address + 2) as u32;

        lo | (hi << 16)
    }

    pub(super) fn load16_io(&mut self, address: u32) -> u16 {
        match address {
            // LCD
            DISPCNT => self.ioregs.dispcnt.into(),
            GREENSWAP => self.ioregs.greenswap,
            DISPSTAT => self.ioregs.dispstat.into(),
            VCOUNT => self.ioregs.vcount,
            BG0CNT => self.ioregs.bgcnt[0].into(),
            BG1CNT => self.ioregs.bgcnt[1].into(),
            BG2CNT => self.ioregs.bgcnt[2].into(),
            BG3CNT => self.ioregs.bgcnt[3].into(),
            BG0HOFS => self.ioregs.bgofs[0].x(),
            BG0VOFS => self.ioregs.bgofs[0].y(),
            BG1HOFS => self.ioregs.bgofs[1].x(),
            BG1VOFS => self.ioregs.bgofs[1].y(),
            BG2HOFS => self.ioregs.bgofs[2].x(),
            BG2VOFS => self.ioregs.bgofs[2].y(),
            BG3HOFS => self.ioregs.bgofs[3].x(),
            BG3VOFS => self.ioregs.bgofs[3].y(),
            BLDCNT => self.ioregs.bldcnt.into(),
            BLDALPHA => self.ioregs.bldalpha.into(),
            BLDY => self.ioregs.bldy.into(),

            // Keypad Input
            KEYINPUT => self.ioregs.keyinput,

            WAITCNT => self.ioregs.waitcnt.into(),
            _ => {
                log::warn!("attempted to read from unused IO address 0x{:08X}", address);
                0
            }
        }
    }

    pub(super) fn load8_io(&mut self, address: u32) -> u8 {
        let shift = (address & 1) * 8;
        (self.load16_io(address & !0x1) >> shift) as u8
    }

    pub(super) fn store32_io(&mut self, address: u32, value: u32) {
        self.store16_io(address, value as u16);
        self.store16_io(address + 2, (value >> 16) as u16);
    }

    pub(super) fn store16_io(&mut self, address: u32, value: u16) {
        match address {
            // LCD
            DISPCNT => self.ioregs.dispcnt.set_preserve_bits(value),
            GREENSWAP => self.ioregs.greenswap = value,
            DISPSTAT => self.ioregs.dispstat.set_preserve_bits(value),
            VCOUNT => { /* NOP */ }
            BG0CNT => self.ioregs.bgcnt[0].set_preserve_bits(value),
            BG1CNT => self.ioregs.bgcnt[1].set_preserve_bits(value),
            BG2CNT => self.ioregs.bgcnt[2].set_preserve_bits(value),
            BG3CNT => self.ioregs.bgcnt[3].set_preserve_bits(value),
            BG0HOFS => self.ioregs.bgofs[0].set_x(value),
            BG0VOFS => self.ioregs.bgofs[0].set_y(value),
            BG1HOFS => self.ioregs.bgofs[1].set_x(value),
            BG1VOFS => self.ioregs.bgofs[1].set_y(value),
            BG2HOFS => self.ioregs.bgofs[2].set_x(value),
            BG2VOFS => self.ioregs.bgofs[2].set_y(value),
            BG3HOFS => self.ioregs.bgofs[3].set_x(value),
            BG3VOFS => self.ioregs.bgofs[3].set_y(value),
            BLDCNT => self.ioregs.bldcnt.set_preserve_bits(value),
            BLDALPHA => self.ioregs.bldalpha.set_preserve_bits(value),
            BLDY => self.ioregs.bldy.set_preserve_bits(value),

            // Keypad Input
            KEYINPUT => { /*NOP */ }

            WAITCNT => {
                self.ioregs.waitcnt.set_preserve_bits(value);
                self.update_waitcnt();
            }

            _ => {
                log::warn!(
                    "attempted to write 0x{:04X} to unused IO address 0x{:08X}",
                    value,
                    address
                );
            }
        }
    }

    pub(super) fn store8_io(&mut self, address: u32, value: u8) {
        let mut value16 = self.view16_io(address);
        let shift = (address & 1) * 8;
        value16 &= !0xFF << shift;
        value16 |= (value as u16) << shift;
        self.store16_io(address, value16)
    }

    pub(super) fn view32_io(&self, address: u32) -> u32 {
        let lo = self.view16_io(address) as u32;
        let hi = self.view16_io(address + 2) as u32;

        lo | (hi << 16)
    }

    pub(super) fn view16_io(&self, address: u32) -> u16 {
        match address {
            // LCD
            DISPCNT => self.ioregs.dispcnt.into(),
            GREENSWAP => self.ioregs.greenswap,
            DISPSTAT => self.ioregs.dispstat.into(),
            VCOUNT => self.ioregs.vcount,
            BG0CNT => self.ioregs.bgcnt[0].into(),
            BG1CNT => self.ioregs.bgcnt[1].into(),
            BG2CNT => self.ioregs.bgcnt[2].into(),
            BG3CNT => self.ioregs.bgcnt[3].into(),
            BG0HOFS => self.ioregs.bgofs[0].x(),
            BG0VOFS => self.ioregs.bgofs[0].y(),
            BG1HOFS => self.ioregs.bgofs[1].x(),
            BG1VOFS => self.ioregs.bgofs[1].y(),
            BG2HOFS => self.ioregs.bgofs[2].x(),
            BG2VOFS => self.ioregs.bgofs[2].y(),
            BG3HOFS => self.ioregs.bgofs[3].x(),
            BG3VOFS => self.ioregs.bgofs[3].y(),
            BLDCNT => self.ioregs.bldcnt.into(),
            BLDALPHA => self.ioregs.bldalpha.into(),
            BLDY => self.ioregs.bldy.into(),

            // Keypad Input
            KEYINPUT => self.ioregs.keyinput,

            WAITCNT => self.ioregs.waitcnt.into(),
            _ => 0,
        }
    }

    pub(super) fn view8_io(&self, address: u32) -> u8 {
        (self.view16_io(address) >> ((address & 1) * 8)) as u8
    }

    pub(super) fn update_waitcnt(&mut self) {
        // NOTE: subtract 1 from cycles to get number of wait states.
        //
        //  Bit   Expl.
        //  0-1   SRAM Wait Control          (0..3 = 4,3,2,8 cycles)
        //  2-3   Wait State 0 First Access  (0..3 = 4,3,2,8 cycles)
        //  4     Wait State 0 Second Access (0..1 = 2,1 cycles)
        //  5-6   Wait State 1 First Access  (0..3 = 4,3,2,8 cycles)
        //  7     Wait State 1 Second Access (0..1 = 4,1 cycles; unlike above WS0)
        //  8-9   Wait State 2 First Access  (0..3 = 4,3,2,8 cycles)
        //  10    Wait State 2 Second Access (0..1 = 8,1 cycles; unlike above WS0,WS1)
        //  11-12 PHI Terminal Output        (0..3 = Disable, 4.19MHz, 8.38MHz, 16.78MHz)
        //  13    Not used
        //  14    Game Pak Prefetch Buffer (Pipe) (0=Disable, 1=Enable)
        //  15    Game Pak Type Flag  (Read Only) (0=GBA, 1=CGB) (IN35 signal)
        //  16-31 Not used

        const SRAM_WAIT_VALUES: [u8; 4] = [3, 2, 1, 7];

        const WS0_NONSEQ_VALUES: [u8; 4] = [3, 2, 1, 7];
        const WS0_SEQ_VALUES: [u8; 2] = [1, 0];

        const WS1_NONSEQ_VALUES: [u8; 4] = [3, 2, 1, 7];
        const WS1_SEQ_VALUES: [u8; 2] = [3, 0];

        const WS2_NONSEQ_VALUES: [u8; 4] = [3, 2, 1, 7];
        const WS2_SEQ_VALUES: [u8; 2] = [7, 0];

        let waitcnt = self.ioregs.waitcnt.value;

        self.sram_waitstates = SRAM_WAIT_VALUES[waitcnt.bits(0, 1) as usize].into();

        self.gamepak_waitstates[0] = WS0_NONSEQ_VALUES[waitcnt.bits(2, 3) as usize].into();
        self.gamepak_waitstates[1] = WS0_SEQ_VALUES[waitcnt.bit(4) as usize].into();

        self.gamepak_waitstates[2] = WS1_NONSEQ_VALUES[waitcnt.bits(5, 6) as usize].into();
        self.gamepak_waitstates[3] = WS1_SEQ_VALUES[waitcnt.bit(7) as usize].into();

        self.gamepak_waitstates[4] = WS2_NONSEQ_VALUES[waitcnt.bits(8, 9) as usize].into();
        self.gamepak_waitstates[5] = WS2_SEQ_VALUES[waitcnt.bit(10) as usize].into();

        self.prefetch_enabled = waitcnt.is_bit_set(14);
    }
}

#[derive(Default)]
pub struct IoRegisters {
    // LCD
    pub(crate) dispcnt: LCDControl,
    pub(crate) greenswap: u16,
    pub(crate) dispstat: LCDStatus,
    pub(crate) waitcnt: WaitstateControl,
    pub(crate) vcount: u16,
    pub(crate) bldcnt: ColorSpecialEffects,
    pub(crate) bldalpha: AlphaBlendingCoeff,
    pub(crate) bldy: BrightnessCoeff,
    pub(crate) bgcnt: [BgControl; 4],
    pub(crate) bgofs: [BgOffset; 4],

    // Keypad Input
    pub(crate) keyinput: u16,
}

impl IoRegisters {
    pub fn init(&mut self) {
        self.keyinput = 0x3ff;
    }
}
<commit_msg>Make BG offset registers readonly.<commit_after>mod constants;
mod types;

use super::GbaMemory;
pub use constants::*;
pub use types::*;
use util::bits::Bits as _;

impl GbaMemory {
    pub(super) fn load32_io(&mut self, address: u32) -> u32 {
        let lo = self.load16_io(address) as u32;
        let hi = self.load16_io(address + 2) as u32;

        lo | (hi << 16)
    }

    pub(super) fn load16_io(&mut self, address: u32) -> u16 {
        match address {
            // LCD
            DISPCNT => self.ioregs.dispcnt.into(),
            GREENSWAP => self.ioregs.greenswap,
            DISPSTAT => self.ioregs.dispstat.into(),
            VCOUNT => self.ioregs.vcount,
            BG0CNT => self.ioregs.bgcnt[0].into(),
            BG1CNT => self.ioregs.bgcnt[1].into(),
            BG2CNT => self.ioregs.bgcnt[2].into(),
            BG3CNT => self.ioregs.bgcnt[3].into(),
            BLDCNT => self.ioregs.bldcnt.into(),
            BLDALPHA => self.ioregs.bldalpha.into(),
            BLDY => self.ioregs.bldy.into(),

            // Keypad Input
            KEYINPUT => self.ioregs.keyinput,

            WAITCNT => self.ioregs.waitcnt.into(),
            _ => {
                log::warn!(
                    "attempted to read from unused/readonly IO address 0x{:08X}",
                    address
                );
                0
            }
        }
    }

    pub(super) fn load8_io(&mut self, address: u32) -> u8 {
        let shift = (address & 1) * 8;
        (self.load16_io(address & !0x1) >> shift) as u8
    }

    pub(super) fn store32_io(&mut self, address: u32, value: u32) {
        self.store16_io(address, value as u16);
        self.store16_io(address + 2, (value >> 16) as u16);
    }

    pub(super) fn store16_io(&mut self, address: u32, value: u16) {
        match address {
            // LCD
            DISPCNT => self.ioregs.dispcnt.set_preserve_bits(value),
            GREENSWAP => self.ioregs.greenswap = value,
            DISPSTAT => self.ioregs.dispstat.set_preserve_bits(value),
            VCOUNT => { /* NOP */ }
            BG0CNT => self.ioregs.bgcnt[0].set_preserve_bits(value),
            BG1CNT => self.ioregs.bgcnt[1].set_preserve_bits(value),
            BG2CNT => self.ioregs.bgcnt[2].set_preserve_bits(value),
            BG3CNT => self.ioregs.bgcnt[3].set_preserve_bits(value),
            BG0HOFS => self.ioregs.bgofs[0].set_x(value),
            BG0VOFS => self.ioregs.bgofs[0].set_y(value),
            BG1HOFS => self.ioregs.bgofs[1].set_x(value),
            BG1VOFS => self.ioregs.bgofs[1].set_y(value),
            BG2HOFS => self.ioregs.bgofs[2].set_x(value),
            BG2VOFS => self.ioregs.bgofs[2].set_y(value),
            BG3HOFS => self.ioregs.bgofs[3].set_x(value),
            BG3VOFS => self.ioregs.bgofs[3].set_y(value),
            BLDCNT => self.ioregs.bldcnt.set_preserve_bits(value),
            BLDALPHA => self.ioregs.bldalpha.set_preserve_bits(value),
            BLDY => self.ioregs.bldy.set_preserve_bits(value),

            // Keypad Input
            KEYINPUT => { /*NOP */ }

            WAITCNT => {
                self.ioregs.waitcnt.set_preserve_bits(value);
                self.update_waitcnt();
            }

            _ => {
                log::warn!(
                    "attempted to write 0x{:04X} to unused IO address 0x{:08X}",
                    value,
                    address
                );
            }
        }
    }

    pub(super) fn store8_io(&mut self, address: u32, value: u8) {
        let mut value16 = self.view16_io(address);
        let shift = (address & 1) * 8;
        value16 &= !0xFF << shift;
        value16 |= (value as u16) << shift;
        self.store16_io(address, value16)
    }

    pub(super) fn view32_io(&self, address: u32) -> u32 {
        let lo = self.view16_io(address) as u32;
        let hi = self.view16_io(address + 2) as u32;

        lo | (hi << 16)
    }

    pub(super) fn view16_io(&self, address: u32) -> u16 {
        match address {
            // LCD
            DISPCNT => self.ioregs.dispcnt.into(),
            GREENSWAP => self.ioregs.greenswap,
            DISPSTAT => self.ioregs.dispstat.into(),
            VCOUNT => self.ioregs.vcount,
            BG0CNT => self.ioregs.bgcnt[0].into(),
            BG1CNT => self.ioregs.bgcnt[1].into(),
            BG2CNT => self.ioregs.bgcnt[2].into(),
            BG3CNT => self.ioregs.bgcnt[3].into(),
            BG0HOFS => self.ioregs.bgofs[0].x(),
            BG0VOFS => self.ioregs.bgofs[0].y(),
            BG1HOFS => self.ioregs.bgofs[1].x(),
            BG1VOFS => self.ioregs.bgofs[1].y(),
            BG2HOFS => self.ioregs.bgofs[2].x(),
            BG2VOFS => self.ioregs.bgofs[2].y(),
            BG3HOFS => self.ioregs.bgofs[3].x(),
            BG3VOFS => self.ioregs.bgofs[3].y(),
            BLDCNT => self.ioregs.bldcnt.into(),
            BLDALPHA => self.ioregs.bldalpha.into(),
            BLDY => self.ioregs.bldy.into(),

            // Keypad Input
            KEYINPUT => self.ioregs.keyinput,

            WAITCNT => self.ioregs.waitcnt.into(),
            _ => 0,
        }
    }

    pub(super) fn view8_io(&self, address: u32) -> u8 {
        (self.view16_io(address) >> ((address & 1) * 8)) as u8
    }

    pub(super) fn update_waitcnt(&mut self) {
        // NOTE: subtract 1 from cycles to get number of wait states.
        //
        //  Bit   Expl.
        //  0-1   SRAM Wait Control          (0..3 = 4,3,2,8 cycles)
        //  2-3   Wait State 0 First Access  (0..3 = 4,3,2,8 cycles)
        //  4     Wait State 0 Second Access (0..1 = 2,1 cycles)
        //  5-6   Wait State 1 First Access  (0..3 = 4,3,2,8 cycles)
        //  7     Wait State 1 Second Access (0..1 = 4,1 cycles; unlike above WS0)
        //  8-9   Wait State 2 First Access  (0..3 = 4,3,2,8 cycles)
        //  10    Wait State 2 Second Access (0..1 = 8,1 cycles; unlike above WS0,WS1)
        //  11-12 PHI Terminal Output        (0..3 = Disable, 4.19MHz, 8.38MHz, 16.78MHz)
        //  13    Not used
        //  14    Game Pak Prefetch Buffer (Pipe) (0=Disable, 1=Enable)
        //  15    Game Pak Type Flag  (Read Only) (0=GBA, 1=CGB) (IN35 signal)
        //  16-31 Not used

        const SRAM_WAIT_VALUES: [u8; 4] = [3, 2, 1, 7];

        const WS0_NONSEQ_VALUES: [u8; 4] = [3, 2, 1, 7];
        const WS0_SEQ_VALUES: [u8; 2] = [1, 0];

        const WS1_NONSEQ_VALUES: [u8; 4] = [3, 2, 1, 7];
        const WS1_SEQ_VALUES: [u8; 2] = [3, 0];

        const WS2_NONSEQ_VALUES: [u8; 4] = [3, 2, 1, 7];
        const WS2_SEQ_VALUES: [u8; 2] = [7, 0];

        let waitcnt = self.ioregs.waitcnt.value;

        self.sram_waitstates = SRAM_WAIT_VALUES[waitcnt.bits(0, 1) as usize].into();

        self.gamepak_waitstates[0] = WS0_NONSEQ_VALUES[waitcnt.bits(2, 3) as usize].into();
        self.gamepak_waitstates[1] = WS0_SEQ_VALUES[waitcnt.bit(4) as usize].into();

        self.gamepak_waitstates[2] = WS1_NONSEQ_VALUES[waitcnt.bits(5, 6) as usize].into();
        self.gamepak_waitstates[3] = WS1_SEQ_VALUES[waitcnt.bit(7) as usize].into();

        self.gamepak_waitstates[4] = WS2_NONSEQ_VALUES[waitcnt.bits(8, 9) as usize].into();
        self.gamepak_waitstates[5] = WS2_SEQ_VALUES[waitcnt.bit(10) as usize].into();

        self.prefetch_enabled = waitcnt.is_bit_set(14);
    }
}

#[derive(Default)]
pub struct IoRegisters {
    // LCD
    pub(crate) dispcnt: LCDControl,
    pub(crate) greenswap: u16,
    pub(crate) dispstat: LCDStatus,
    pub(crate) waitcnt: WaitstateControl,
    pub(crate) vcount: u16,
    pub(crate) bldcnt: ColorSpecialEffects,
    pub(crate) bldalpha: AlphaBlendingCoeff,
    pub(crate) bldy: BrightnessCoeff,
    pub(crate) bgcnt: [BgControl; 4],
    pub(crate) bgofs: [BgOffset; 4],

    // Keypad Input
    pub(crate) keyinput: u16,
}

impl IoRegisters {
    pub fn init(&mut self) {
        self.keyinput = 0x3ff;
    }
}
<|endoftext|>


<commit_before>//! HIR (previsouly known as descriptors) provides a high-level OO acess to Rust
//! code.
//!
//! The principal difference between HIR and syntax trees is that HIR is bound
//! to a particular crate instance. That is, it has cfg flags and features
//! applied. So, there relation between syntax and HIR is many-to-one.

macro_rules! ctry {
    ($expr:expr) => {
        match $expr {
            None => return Ok(None),
            Some(it) => it,
        }
    };
}

pub mod db;
#[cfg(test)]
mod mock;
mod query_definitions;
mod path;
mod arena;
pub mod source_binder;

mod krate;
mod module;
mod function;

use std::ops::Index;

use ra_syntax::{SyntaxNodeRef, SyntaxNode};
use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};

use crate::{
    db::HirDatabase,
    arena::{Arena, Id},
};

pub use self::{
    path::{Path, PathKind},
    krate::Crate,
    module::{Module, ModuleId, Problem, nameres::ItemMap},
    function::{Function, FnScopes},
};

pub use self::function::FnSignatureInfo;

/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
/// in a specific module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DefId(u32);
ra_db::impl_numeric_id!(DefId);

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum DefKind {
    Module,
    Function,
    Item,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct DefLoc {
    pub(crate) kind: DefKind,
    source_root_id: SourceRootId,
    module_id: ModuleId,
    source_item_id: SourceItemId,
}

impl DefId {
    pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
        db.as_ref().id2loc(self)
    }
}

impl DefLoc {
    pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
        db.as_ref().loc2id(&self)
    }
}

pub enum Def {
    Module(Module),
    Function(Function),
    Item,
}

impl DefId {
    pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
        let loc = self.loc(db);
        let res = match loc.kind {
            DefKind::Module => {
                let module = Module::new(db, loc.source_root_id, loc.module_id)?;
                Def::Module(module)
            }
            DefKind::Function => {
                let function = Function::new(self);
                Def::Function(function)
            }
            DefKind::Item => Def::Item,
        };
        Ok(res)
    }
}

/// Identifier of item within a specific file. This is stable over reparses, so
/// it's OK to use it as a salsa key/value.
pub(crate) type SourceFileItemId = Id<SyntaxNode>;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SourceItemId {
    file_id: FileId,
    item_id: SourceFileItemId,
}

/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
#[derive(Debug, PartialEq, Eq)]
pub struct SourceFileItems {
    file_id: FileId,
    arena: Arena<SyntaxNode>,
}

impl SourceFileItems {
    fn new(file_id: FileId) -> SourceFileItems {
        SourceFileItems {
            file_id,
            arena: Arena::default(),
        }
    }

    fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
        self.arena.alloc(item)
    }
    pub fn id_of(&self, file_id: FileId, item: SyntaxNodeRef) -> SourceFileItemId {
        assert_eq!(
            self.file_id, file_id,
            "SourceFileItems: wrong file, expected {:?}, got {:?}",
            self.file_id, file_id
        );
        self.id_of_unchecked(item)
    }
    fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId {
        if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) {
            return id;
        }
        // This should not happen. Let's try to give a sensible diagnostics.
        if let Some((_, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
            panic!(
                "unequal syntax nodes with the same range:\n{:?}\n{:?}",
                item, i
            )
        }
        panic!(
            "Can't find {:?} in SourceFileItems:\n{:?}",
            item,
            self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
        );
    }
    pub fn id_of_source_file(&self) -> SourceFileItemId {
        let (id, _syntax) = self.arena.iter().next().unwrap();
        id
    }
}

impl Index<SourceFileItemId> for SourceFileItems {
    type Output = SyntaxNode;
    fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
        &self.arena[idx]
    }
}
<commit_msg>Work around a bug<commit_after>//! HIR (previsouly known as descriptors) provides a high-level OO acess to Rust
//! code.
//!
//! The principal difference between HIR and syntax trees is that HIR is bound
//! to a particular crate instance. That is, it has cfg flags and features
//! applied. So, there relation between syntax and HIR is many-to-one.

macro_rules! ctry {
    ($expr:expr) => {
        match $expr {
            None => return Ok(None),
            Some(it) => it,
        }
    };
}

pub mod db;
#[cfg(test)]
mod mock;
mod query_definitions;
mod path;
mod arena;
pub mod source_binder;

mod krate;
mod module;
mod function;

use std::ops::Index;

use ra_syntax::{SyntaxNodeRef, SyntaxNode};
use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};

use crate::{
    db::HirDatabase,
    arena::{Arena, Id},
};

pub use self::{
    path::{Path, PathKind},
    krate::Crate,
    module::{Module, ModuleId, Problem, nameres::ItemMap},
    function::{Function, FnScopes},
};

pub use self::function::FnSignatureInfo;

/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
/// in a specific module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DefId(u32);
ra_db::impl_numeric_id!(DefId);

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum DefKind {
    Module,
    Function,
    Item,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct DefLoc {
    pub(crate) kind: DefKind,
    source_root_id: SourceRootId,
    module_id: ModuleId,
    source_item_id: SourceItemId,
}

impl DefId {
    pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
        db.as_ref().id2loc(self)
    }
}

impl DefLoc {
    pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
        db.as_ref().loc2id(&self)
    }
}

pub enum Def {
    Module(Module),
    Function(Function),
    Item,
}

impl DefId {
    pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
        let loc = self.loc(db);
        let res = match loc.kind {
            DefKind::Module => {
                let module = Module::new(db, loc.source_root_id, loc.module_id)?;
                Def::Module(module)
            }
            DefKind::Function => {
                let function = Function::new(self);
                Def::Function(function)
            }
            DefKind::Item => Def::Item,
        };
        Ok(res)
    }
}

/// Identifier of item within a specific file. This is stable over reparses, so
/// it's OK to use it as a salsa key/value.
pub(crate) type SourceFileItemId = Id<SyntaxNode>;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SourceItemId {
    file_id: FileId,
    item_id: SourceFileItemId,
}

/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
#[derive(Debug, PartialEq, Eq)]
pub struct SourceFileItems {
    file_id: FileId,
    arena: Arena<SyntaxNode>,
}

impl SourceFileItems {
    fn new(file_id: FileId) -> SourceFileItems {
        SourceFileItems {
            file_id,
            arena: Arena::default(),
        }
    }

    fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
        self.arena.alloc(item)
    }
    pub fn id_of(&self, file_id: FileId, item: SyntaxNodeRef) -> SourceFileItemId {
        assert_eq!(
            self.file_id, file_id,
            "SourceFileItems: wrong file, expected {:?}, got {:?}",
            self.file_id, file_id
        );
        self.id_of_unchecked(item)
    }
    fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId {
        if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) {
            return id;
        }
        // This should not happen. Let's try to give a sensible diagnostics.
        if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
            // FIXME(#288): whyyy are we getting here?
            log::error!(
                "unequal syntax nodes with the same range:\n{:?}\n{:?}",
                item,
                i
            );
            return id;
        }
        panic!(
            "Can't find {:?} in SourceFileItems:\n{:?}",
            item,
            self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
        );
    }
    pub fn id_of_source_file(&self) -> SourceFileItemId {
        let (id, _syntax) = self.arena.iter().next().unwrap();
        id
    }
}

impl Index<SourceFileItemId> for SourceFileItems {
    type Output = SyntaxNode;
    fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
        &self.arena[idx]
    }
}
<|endoftext|>


<commit_before>use crate::grammar::ast::{eq::AstEq, Block, Expression, Statement};
use crate::grammar::model::{Fragment, HasFragment};
use crate::grammar::parsers::expression::ToExpression;
use crate::grammar::parsers::whitespace::token_delimiter;
use crate::grammar::parsers::with_input;
use nom::bytes::complete::tag;
use nom::combinator::{map, opt};
use nom::multi::many0;
use nom::sequence::{delimited, pair, terminated};
use nom::IResult;

impl<'s> Block<'s> {
    /// Block delimiters. Should not change.
    pub const DELIMITERS: (&'static str, &'static str) = ("{", "}");

    fn inner(
        input: Fragment<'s>,
    ) -> IResult<Fragment<'s>, (Vec<Statement<'s>>, Option<Box<Expression<'s>>>)> {
        pair(
            many0(terminated(Statement::parse, token_delimiter)),
            opt(map(Expression::parse, |e| Box::new(e))),
        )(input)
    }

    /// Parse a block in source code.
    pub fn parse(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            with_input(delimited(
                pair(tag(Self::DELIMITERS.0), token_delimiter),
                Self::inner,
                pair(token_delimiter, tag(Self::DELIMITERS.1)),
            )),
            |(consumed, (statements, terminal))| Self {
                frag: consumed,
                statements,
                result: terminal,
            },
        )(input)
    }
}

impl<'s> HasFragment<'s> for Block<'s> {
    fn get_fragment(&self) -> Fragment<'s> {
        self.frag
    }
}

impl<'s> ToExpression<'s> for Block<'s> {
    fn create_expr(self) -> Expression<'s> {
        Expression::Block(self)
    }
}

impl<'s> AstEq for Block<'s> {
    fn ast_eq(fst: &Self, snd: &Self) -> bool {
        AstEq::ast_eq(&fst.result, &snd.result) && AstEq::ast_eq(&fst.statements, &snd.statements)
    }
}
<commit_msg>minor refactor<commit_after>use crate::grammar::ast::{eq::AstEq, Block, Expression, Statement};
use crate::grammar::model::{Fragment, HasFragment};
use crate::grammar::parsers::expression::ToExpression;
use crate::grammar::parsers::whitespace::token_delimiter;
use crate::grammar::parsers::with_input;
use nom::bytes::complete::tag;
use nom::combinator::{map, opt};
use nom::multi::many0;
use nom::sequence::{delimited, pair, terminated};
use nom::IResult;

impl<'s> Block<'s> {
    /// Start of block in source code.
    pub const START_DELIMITER: &'static str = "{";
    /// End of a block in source code.
    pub const END_DELIMITER: &'static str = "}";

    fn inner(
        input: Fragment<'s>,
    ) -> IResult<Fragment<'s>, (Vec<Statement<'s>>, Option<Box<Expression<'s>>>)> {
        pair(
            many0(terminated(Statement::parse, token_delimiter)),
            opt(map(Expression::parse, |e| Box::new(e))),
        )(input)
    }

    /// Parse a block in source code.
    pub fn parse(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            with_input(delimited(
                pair(tag(Self::START_DELIMITER), token_delimiter),
                Self::inner,
                pair(token_delimiter, tag(Self::END_DELIMITER)),
            )),
            |(consumed, (statements, terminal))| Self {
                frag: consumed,
                statements,
                result: terminal,
            },
        )(input)
    }
}

impl<'s> HasFragment<'s> for Block<'s> {
    fn get_fragment(&self) -> Fragment<'s> {
        self.frag
    }
}

impl<'s> ToExpression<'s> for Block<'s> {
    fn create_expr(self) -> Expression<'s> {
        Expression::Block(self)
    }
}

impl<'s> AstEq for Block<'s> {
    fn ast_eq(fst: &Self, snd: &Self) -> bool {
        AstEq::ast_eq(&fst.result, &snd.result) && AstEq::ast_eq(&fst.statements, &snd.statements)
    }
}
<|endoftext|>


<commit_before>use clap::ArgMatches;
use clap_utils::{
    parse_optional, parse_path_with_default_in_home_dir, parse_required, parse_ssz_optional,
};
use eth2_testnet_config::Eth2TestnetConfig;
use std::path::PathBuf;
use types::{Address, EthSpec, YamlConfig};

pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
    let testnet_dir_path = parse_path_with_default_in_home_dir(
        matches,
        "testnet-dir",
        PathBuf::from(".lighthouse/testnet"),
    )?;
    let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?;
    let deposit_contract_deploy_block = parse_required(matches, "deposit-contract-deploy-block")?;

    let overwrite_files = matches.is_present("force");

    if testnet_dir_path.exists() {
        if !overwrite_files {
            return Err(format!(
                "{:?} already exists, will not overwrite. Use --force to overwrite",
                testnet_dir_path
            ));
        }
    }

    let mut spec = T::default_spec();

    // Update the spec value if the flag was defined. Otherwise, leave it as the default.
    macro_rules! maybe_update {
        ($flag: tt, $var: ident) => {
            if let Some(val) = parse_optional(matches, $flag)? {
                spec.$var = val
            }
        };
    }

    maybe_update!("min-genesis-time", min_genesis_time);
    maybe_update!("min-deposit-amount", min_deposit_amount);
    maybe_update!(
        "min-genesis-active-validator-count",
        min_genesis_active_validator_count
    );
    maybe_update!("max-effective-balance", max_effective_balance);
    maybe_update!("effective-balance-increment", effective_balance_increment);
    maybe_update!("ejection-balance", ejection_balance);
    maybe_update!("eth1-follow_distance", eth1_follow_distance);
    maybe_update!("min-genesis-delay", min_genesis_delay);

    if let Some(v) = parse_ssz_optional(matches, "genesis-fork-version")? {
        spec.genesis_fork_version = v;
    }

    let testnet: Eth2TestnetConfig<T> = Eth2TestnetConfig {
        deposit_contract_address: format!("{:?}", deposit_contract_address),
        deposit_contract_deploy_block,
        boot_enr: Some(vec![]),
        genesis_state: None,
        yaml_config: Some(YamlConfig::from_spec::<T>(&spec)),
    };

    testnet.write_to_file(testnet_dir_path, overwrite_files)
}
<commit_msg>Fix lcli arg typo (#1097)<commit_after>use clap::ArgMatches;
use clap_utils::{
    parse_optional, parse_path_with_default_in_home_dir, parse_required, parse_ssz_optional,
};
use eth2_testnet_config::Eth2TestnetConfig;
use std::path::PathBuf;
use types::{Address, EthSpec, YamlConfig};

pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
    let testnet_dir_path = parse_path_with_default_in_home_dir(
        matches,
        "testnet-dir",
        PathBuf::from(".lighthouse/testnet"),
    )?;
    let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?;
    let deposit_contract_deploy_block = parse_required(matches, "deposit-contract-deploy-block")?;

    let overwrite_files = matches.is_present("force");

    if testnet_dir_path.exists() {
        if !overwrite_files {
            return Err(format!(
                "{:?} already exists, will not overwrite. Use --force to overwrite",
                testnet_dir_path
            ));
        }
    }

    let mut spec = T::default_spec();

    // Update the spec value if the flag was defined. Otherwise, leave it as the default.
    macro_rules! maybe_update {
        ($flag: tt, $var: ident) => {
            if let Some(val) = parse_optional(matches, $flag)? {
                spec.$var = val
            }
        };
    }

    maybe_update!("min-genesis-time", min_genesis_time);
    maybe_update!("min-deposit-amount", min_deposit_amount);
    maybe_update!(
        "min-genesis-active-validator-count",
        min_genesis_active_validator_count
    );
    maybe_update!("max-effective-balance", max_effective_balance);
    maybe_update!("effective-balance-increment", effective_balance_increment);
    maybe_update!("ejection-balance", ejection_balance);
    maybe_update!("eth1-follow-distance", eth1_follow_distance);
    maybe_update!("min-genesis-delay", min_genesis_delay);

    if let Some(v) = parse_ssz_optional(matches, "genesis-fork-version")? {
        spec.genesis_fork_version = v;
    }

    let testnet: Eth2TestnetConfig<T> = Eth2TestnetConfig {
        deposit_contract_address: format!("{:?}", deposit_contract_address),
        deposit_contract_deploy_block,
        boot_enr: Some(vec![]),
        genesis_state: None,
        yaml_config: Some(YamlConfig::from_spec::<T>(&spec)),
    };

    testnet.write_to_file(testnet_dir_path, overwrite_files)
}
<|endoftext|>


<commit_before>use syn::{self, Token, TokenTree, Ident, BinOpToken, DelimToken};
use quote::{Tokens, ToTokens};
use std::collections::HashMap;

#[derive(Debug)]
pub struct Args<'a> {
    input: &'a str,
    pub tokens: Tokens,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Type {
    Expr,
    Closure,
    List,
    Object,
}

#[derive(Debug, Clone)]
enum Group {
    Expr(Vec<TokenTree>),
    Closure(Vec<TokenTree>),
    List(Vec<Group>),
    Object(HashMap<String, Group>),
}

impl<'a> Args<'a> {
    pub fn new(input: &str) -> Args {
        Args {
            input: input.trim(),
            tokens: Tokens::new(),
        }
    }

    pub fn process(&mut self) -> &mut Self {
        if self.input.is_empty() {
            self.tokens = quote!(reql::Term::new());
            return self;
        }

        let args = format!("args!({})", self.input);
        let body = self.body();

        self.tokens = quote!({
            #[allow(unused_imports)]
            use reql::{ToArg, Args, Term};

            let mut args = Args::new();
            args.set_string(#args);
            #body
            args
        });

        self
    }

    fn body(&mut self) -> Tokens {
        let tt = syn::parse_token_trees(self.input).expect("failed to parse token tree");
        let mut tokens = Tokens::new();
        let args = self.group_by_comma(tt);
        let last = args.len()-1;
        for (i, arg) in args.into_iter().enumerate() {
            arg.tokenise("args", i == last).to_tokens(&mut tokens);
        }
        tokens
    }

    fn finalise_arg(&self, args: &mut Vec<Group>, tokens: &mut Vec<TokenTree>, typ: &mut Type) {
        let group = match *typ {
            Type::Expr => Group::Expr(tokens.clone()),
            Type::Closure => Group::Closure(tokens.clone()),
            Type::List => Group::List(self.group_by_comma(tokens.clone())),
            Type::Object => Group::Object(self.hash_from_tokens(tokens.clone())),
        };
        args.push(group);
        *tokens = Vec::new();
        *typ = Type::Expr;
    }

    fn hash_from_tokens(&self, tt: Vec<TokenTree>) -> HashMap<String, Group> {
        let mut hash = HashMap::new();

        let mut is_key = true;
        let mut key = String::new();
        let mut val = Vec::new();
        let last = tt.len()-1;

        for (i, tree) in tt.into_iter().enumerate() {
            if let TokenTree::Token(Token::Colon) = tree {
                if key.is_empty() {
                    panic!("A key of an object cannot be empty.");
                }
                is_key = false;
                continue;
            }
            if is_key {
                key = {
                    let mut tokens = Tokens::new();
                    tree.to_tokens(&mut tokens);
                    tokens.to_string()
                };
                continue;
            }
            let token_is_comma = if let TokenTree::Token(Token::Comma) = tree { true } else { false };
            let on_last_token = if i == last { true } else { false };
            let val_end = token_is_comma || on_last_token;
            if !token_is_comma {
                val.push(tree);
            }
            if val_end {
                let arg = self.group_by_comma(val.clone());
                let len = arg.len();
                if len == 0 {
                    panic!("An object key cannot have no value.");
                } else if len == 1 {
                    hash.insert(key.clone(), arg.into_iter().next().unwrap());
                } else {
                    panic!("An object key cannot have more than 1 values.");
                }
                is_key = true;
                val = Vec::new();
            }
        }

        hash
    }

    fn group_by_comma(&self, tt: Vec<TokenTree>) -> Vec<Group> {
        let mut args = Vec::new();
        let mut tokens = Vec::new();
        let mut typ = Type::Expr;
        let last = tt.len()-1;

        for (i, tree) in tt.into_iter().enumerate() {
            let token_is_comma = if let TokenTree::Token(Token::Comma) = tree { true } else { false };
            let on_last_token = if i == last { true } else { false };
            let arg_end = token_is_comma || on_last_token;

            match Type::new(&tokens, &tree, &mut typ) {
                Type::Expr => {
                    if !token_is_comma {
                        tokens.push(tree);
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ);
                    }
                }
                Type::Closure => {
                    if !token_is_comma {
                        tokens.push(tree);
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ);
                    }
                }
                Type::List => {
                    if !token_is_comma {
                        if let TokenTree::Delimited(d) = tree {
                            tokens = d.tts;
                        }
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ);
                    }
                }
                Type::Object => {
                    if !token_is_comma {
                        if let TokenTree::Delimited(d) = tree {
                            tokens = d.tts;
                        }
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ);
                    }
                }
            }
        }
        args
    }
}

impl Group {
    fn tokenise(self, var: &str, last: bool) -> Tokens {
        let mut tokens = Tokens::new();
        let var = Ident::from(var);
        match self {
            Group::Expr(tt) => {
                let mut expr = Tokens::new();
                for token in tt {
                    token.to_tokens(&mut expr);
                }
                quote!(#var.add_arg(#expr.to_arg());)
                    .to_tokens(&mut tokens);
            }
            Group::Closure(_closure) => {
                unimplemented!();
            }
            Group::List(tt) => {
                let mut list = quote!(let mut list_arg = Term::new(););
                for group in tt {
                    quote!(let mut list_val = Args::new();)
                        .to_tokens(&mut list);
                    group.tokenise("list_val", false)
                        .to_tokens(&mut list);
                    quote!(list_arg.mut_args().push(list_val.to_arg().term());)
                        .to_tokens(&mut list);
                }
                quote!(#var.mut_term().mut_args().push(list_arg);)
                        .to_tokens(&mut list);
                list.to_tokens(&mut tokens);
            }
            Group::Object(tt) => {
                let mut obj = Tokens::new();
                if !last {
                    quote!(let mut obj_arg = Term::new();)
                        .to_tokens(&mut obj);
                }
                for (key, group) in tt {
                    quote!(let mut obj_val = Args::new();)
                        .to_tokens(&mut obj);
                    group.tokenise("obj_val", false)
                        .to_tokens(&mut obj);
                    quote!(let temp_pair = Args::create_term_pair(#key, obj_val);)
                        .to_tokens(&mut obj);
                    if last {
                        quote!(#var.mut_term().mut_optargs().push(temp_pair);)
                            .to_tokens(&mut obj);
                    } else {
                        quote!(obj_arg.mut_optargs().push(temp_pair);)
                            .to_tokens(&mut obj);
                    }
                }
                if !last {
                    quote!(#var.mut_term().mut_args().push(obj_arg);)
                        .to_tokens(&mut obj);
                }
                obj.to_tokens(&mut tokens);
            }
        }
        tokens
    }
}

impl Type {
    fn new(tokens: &Vec<TokenTree>, tree: &TokenTree, typ: &mut Type) -> Type {
        if tokens.is_empty() {
            if let TokenTree::Delimited(ref d) = *tree {
                if d.delim == DelimToken::Bracket { *typ = Type::List; }
                else if d.delim == DelimToken::Brace { *typ = Type::Object; }
            }
            else if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                *typ = Type::Closure;
            }
        } else if tokens.len() == 1 {
            if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                if let TokenTree::Token(Token::Ident(ref i)) = tokens[0] {
                    if i == "move" {
                        *typ = Type::Closure;
                    }
                }
            }
        }
        *typ
    }
}
<commit_msg>Support JSON-like keys in objects<commit_after>use syn::{self, Token, TokenTree, Ident, BinOpToken, DelimToken};
use quote::{Tokens, ToTokens};
use std::collections::HashMap;

#[derive(Debug)]
pub struct Args<'a> {
    input: &'a str,
    pub tokens: Tokens,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Type {
    Expr,
    Closure,
    List,
    Object,
}

#[derive(Debug, Clone)]
enum Group {
    Expr(Vec<TokenTree>),
    Closure(Vec<TokenTree>),
    List(Vec<Group>),
    Object(HashMap<String, Group>),
}

impl<'a> Args<'a> {
    pub fn new(input: &str) -> Args {
        Args {
            input: input.trim(),
            tokens: Tokens::new(),
        }
    }

    pub fn process(&mut self) -> &mut Self {
        if self.input.is_empty() {
            self.tokens = quote!(reql::Term::new());
            return self;
        }

        let args = format!("args!({})", self.input);
        let body = self.body();

        self.tokens = quote!({
            #[allow(unused_imports)]
            use reql::{IntoArg, Args, Term};

            let mut args = Args::new();
            args.set_string(#args);
            #body
            args
        });

        self
    }

    fn body(&mut self) -> Tokens {
        let tt = syn::parse_token_trees(self.input).expect("failed to parse token tree");
        let mut tokens = Tokens::new();
        let args = self.group_by_comma(tt);
        let last = args.len()-1;
        for (i, arg) in args.into_iter().enumerate() {
            arg.tokenise("args", i == last).to_tokens(&mut tokens);
        }
        tokens
    }

    fn finalise_arg(&self, args: &mut Vec<Group>, tokens: &mut Vec<TokenTree>, typ: &mut Type) {
        let group = match *typ {
            Type::Expr => Group::Expr(tokens.clone()),
            Type::Closure => Group::Closure(tokens.clone()),
            Type::List => Group::List(self.group_by_comma(tokens.clone())),
            Type::Object => Group::Object(self.hash_from_tokens(tokens.clone())),
        };
        args.push(group);
        *tokens = Vec::new();
        *typ = Type::Expr;
    }

    fn hash_from_tokens(&self, tt: Vec<TokenTree>) -> HashMap<String, Group> {
        let mut hash = HashMap::new();

        let mut is_key = true;
        let mut key = String::new();
        let mut val = Vec::new();
        let last = tt.len()-1;

        for (i, tree) in tt.into_iter().enumerate() {
            if let TokenTree::Token(Token::Colon) = tree {
                if key.is_empty() {
                    panic!("A key of an object cannot be empty.");
                }
                is_key = false;
                continue;
            }
            if is_key {
                key = match tree {
                    TokenTree::Token(Token::Literal(syn::Lit::Str(key, _))) => key,
                    tree => {
                        let mut tokens = Tokens::new();
                        tree.to_tokens(&mut tokens);
                        tokens.to_string()
                    }
                };
                continue;
            }
            let token_is_comma = if let TokenTree::Token(Token::Comma) = tree { true } else { false };
            let on_last_token = if i == last { true } else { false };
            let val_end = token_is_comma || on_last_token;
            if !token_is_comma {
                val.push(tree);
            }
            if val_end {
                let arg = self.group_by_comma(val.clone());
                let len = arg.len();
                if len == 0 {
                    panic!("An object key cannot have no value.");
                } else if len == 1 {
                    hash.insert(key.clone(), arg.into_iter().next().unwrap());
                } else {
                    panic!("An object key cannot have more than 1 values.");
                }
                is_key = true;
                val = Vec::new();
            }
        }

        hash
    }

    fn group_by_comma(&self, tt: Vec<TokenTree>) -> Vec<Group> {
        let mut args = Vec::new();
        let mut tokens = Vec::new();
        let mut typ = Type::Expr;
        let last = tt.len()-1;

        for (i, tree) in tt.into_iter().enumerate() {
            let token_is_comma = if let TokenTree::Token(Token::Comma) = tree { true } else { false };
            let on_last_token = if i == last { true } else { false };
            let arg_end = token_is_comma || on_last_token;

            match Type::new(&tokens, &tree, &mut typ) {
                Type::Expr => {
                    if !token_is_comma {
                        tokens.push(tree);
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ);
                    }
                }
                Type::Closure => {
                    if !token_is_comma {
                        tokens.push(tree);
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ);
                    }
                }
                Type::List => {
                    if !token_is_comma {
                        if let TokenTree::Delimited(d) = tree {
                            tokens = d.tts;
                        }
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ);
                    }
                }
                Type::Object => {
                    if !token_is_comma {
                        if let TokenTree::Delimited(d) = tree {
                            tokens = d.tts;
                        }
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ);
                    }
                }
            }
        }
        args
    }
}

impl Group {
    fn tokenise(self, var: &str, last: bool) -> Tokens {
        let mut tokens = Tokens::new();
        let var = Ident::from(var);
        match self {
            Group::Expr(tt) => {
                let mut expr = Tokens::new();
                for token in tt {
                    token.to_tokens(&mut expr);
                }
                quote!(#var.add_arg(#expr.into_arg());)
                    .to_tokens(&mut tokens);
            }
            Group::Closure(_closure) => {
                unimplemented!();
            }
            Group::List(tt) => {
                let mut list = quote!(let mut list_arg = Term::new(););
                for group in tt {
                    quote!(let mut list_val = Args::new();)
                        .to_tokens(&mut list);
                    group.tokenise("list_val", false)
                        .to_tokens(&mut list);
                    quote!(list_arg.mut_args().push(list_val.into_arg().term());)
                        .to_tokens(&mut list);
                }
                quote!(#var.mut_term().mut_args().push(list_arg);)
                        .to_tokens(&mut list);
                list.to_tokens(&mut tokens);
            }
            Group::Object(tt) => {
                let mut obj = Tokens::new();
                if !last {
                    quote!(let mut obj_arg = Term::new();)
                        .to_tokens(&mut obj);
                }
                for (key, group) in tt {
                    quote!(let mut obj_val = Args::new();)
                        .to_tokens(&mut obj);
                    group.tokenise("obj_val", false)
                        .to_tokens(&mut obj);
                    quote!(let temp_pair = Args::create_term_pair(#key, obj_val);)
                        .to_tokens(&mut obj);
                    if last {
                        quote!(#var.mut_term().mut_optargs().push(temp_pair);)
                            .to_tokens(&mut obj);
                    } else {
                        quote!(obj_arg.mut_optargs().push(temp_pair);)
                            .to_tokens(&mut obj);
                    }
                }
                if !last {
                    quote!(#var.mut_term().mut_args().push(obj_arg);)
                        .to_tokens(&mut obj);
                }
                obj.to_tokens(&mut tokens);
            }
        }
        tokens
    }
}

impl Type {
    fn new(tokens: &Vec<TokenTree>, tree: &TokenTree, typ: &mut Type) -> Type {
        if tokens.is_empty() {
            if let TokenTree::Delimited(ref d) = *tree {
                if d.delim == DelimToken::Bracket { *typ = Type::List; }
                else if d.delim == DelimToken::Brace { *typ = Type::Object; }
            }
            else if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                *typ = Type::Closure;
            }
        } else if tokens.len() == 1 {
            if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                if let TokenTree::Token(Token::Ident(ref i)) = tokens[0] {
                    if i == "move" {
                        *typ = Type::Closure;
                    }
                }
            }
        }
        *typ
    }
}
<|endoftext|>