<commit_before>#[inline]
pub fn write_to_vec(vec: &mut Vec<u8>, byte: u8) {
    vec.push(byte);
}

#[cfg(target_pointer_width = "32")]
const USIZE_LEB128_SIZE: usize = 5;
#[cfg(target_pointer_width = "64")]
const USIZE_LEB128_SIZE: usize = 10;

macro_rules! leb128_size {
    (u16) => {
        3
    };
    (u32) => {
        5
    };
    (u64) => {
        10
    };
    (u128) => {
        19
    };
    (usize) => {
        USIZE_LEB128_SIZE
    };
}

macro_rules! impl_write_unsigned_leb128 {
    ($fn_name:ident, $int_ty:ident) => {
        #[inline]
        pub fn $fn_name(out: &mut Vec<u8>, mut value: $int_ty) {
            for _ in 0..leb128_size!($int_ty) {
                let mut byte = (value & 0x7F) as u8;
                value >>= 7;
                if value != 0 {
                    byte |= 0x80;
                }

                write_to_vec(out, byte);

                if value == 0 {
                    break;
                }
            }
        }
    };
}

impl_write_unsigned_leb128!(write_u16_leb128, u16);
impl_write_unsigned_leb128!(write_u32_leb128, u32);
impl_write_unsigned_leb128!(write_u64_leb128, u64);
impl_write_unsigned_leb128!(write_u128_leb128, u128);
impl_write_unsigned_leb128!(write_usize_leb128, usize);

macro_rules! impl_read_unsigned_leb128 {
    ($fn_name:ident, $int_ty:ident) => {
        #[inline]
        pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
            let mut result: $int_ty = 0;
            let mut shift = 0;
            let mut position = 0;

            for _ in 0..leb128_size!($int_ty) {
                let byte = unsafe { *slice.get_unchecked(position) };
                position += 1;
                result |= ((byte & 0x7F) as $int_ty) << shift;
                if (byte & 0x80) == 0 {
                    break;
                }
                shift += 7;
            }

            // Do a single bounds check at the end instead of for every byte.
            assert!(position <= slice.len());

            (result, position)
        }
    };
}

impl_read_unsigned_leb128!(read_u16_leb128, u16);
impl_read_unsigned_leb128!(read_u32_leb128, u32);
impl_read_unsigned_leb128!(read_u64_leb128, u64);
impl_read_unsigned_leb128!(read_u128_leb128, u128);
impl_read_unsigned_leb128!(read_usize_leb128, usize);

#[inline]
/// encodes an integer using signed leb128 encoding and stores
/// the result using a callback function.
///
/// The callback `write` is called once for each position
/// that is to be written to with the byte to be encoded
/// at that position.
pub fn write_signed_leb128_to<W>(mut value: i128, mut write: W)
where
    W: FnMut(u8),
{
    loop {
        let mut byte = (value as u8) & 0x7f;
        value >>= 7;
        let more =
            !(((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && ((byte & 0x40) != 0)));

        if more {
            byte |= 0x80; // Mark this byte to show that more bytes will follow.
        }

        write(byte);

        if !more {
            break;
        }
    }
}

#[inline]
pub fn write_signed_leb128(out: &mut Vec<u8>, value: i128) {
    write_signed_leb128_to(value, |v| write_to_vec(out, v))
}

#[inline]
pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) {
    let mut result = 0;
    let mut shift = 0;
    let mut position = start_position;
    let mut byte;

    loop {
        byte = data[position];
        position += 1;
        result |= i128::from(byte & 0x7F) << shift;
        shift += 7;

        if (byte & 0x80) == 0 {
            break;
        }
    }

    if (shift < 64) && ((byte & 0x40) != 0) {
        // sign extend
        result |= -(1 << shift);
    }

    (result, position - start_position)
}
<commit_msg>Micro-optimize the heck out of LEB128 reading and writing.<commit_after>macro_rules! impl_write_unsigned_leb128 {
    ($fn_name:ident, $int_ty:ident) => {
        #[inline]
        pub fn $fn_name(out: &mut Vec<u8>, mut value: $int_ty) {
            loop {
                if value < 0x80 {
                    out.push(value as u8);
                    break;
                } else {
                    out.push(((value & 0x7f) | 0x80) as u8);
                    value >>= 7;
                }
            }
        }
    };
}

impl_write_unsigned_leb128!(write_u16_leb128, u16);
impl_write_unsigned_leb128!(write_u32_leb128, u32);
impl_write_unsigned_leb128!(write_u64_leb128, u64);
impl_write_unsigned_leb128!(write_u128_leb128, u128);
impl_write_unsigned_leb128!(write_usize_leb128, usize);

macro_rules! impl_read_unsigned_leb128 {
    ($fn_name:ident, $int_ty:ident) => {
        #[inline]
        pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
            let mut result = 0;
            let mut shift = 0;
            let mut position = 0;
            loop {
                let byte = slice[position];
                position += 1;
                if (byte & 0x80) == 0 {
                    result |= (byte as $int_ty) << shift;
                    return (result, position);
                } else {
                    result |= ((byte & 0x7F) as $int_ty) << shift;
                }
                shift += 7;
            }
        }
    };
}

impl_read_unsigned_leb128!(read_u16_leb128, u16);
impl_read_unsigned_leb128!(read_u32_leb128, u32);
impl_read_unsigned_leb128!(read_u64_leb128, u64);
impl_read_unsigned_leb128!(read_u128_leb128, u128);
impl_read_unsigned_leb128!(read_usize_leb128, usize);

#[inline]
/// encodes an integer using signed leb128 encoding and stores
/// the result using a callback function.
///
/// The callback `write` is called once for each position
/// that is to be written to with the byte to be encoded
/// at that position.
pub fn write_signed_leb128_to<W>(mut value: i128, mut write: W)
where
    W: FnMut(u8),
{
    loop {
        let mut byte = (value as u8) & 0x7f;
        value >>= 7;
        let more =
            !(((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && ((byte & 0x40) != 0)));

        if more {
            byte |= 0x80; // Mark this byte to show that more bytes will follow.
        }

        write(byte);

        if !more {
            break;
        }
    }
}

#[inline]
pub fn write_signed_leb128(out: &mut Vec<u8>, value: i128) {
    write_signed_leb128_to(value, |v| out.push(v))
}

#[inline]
pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) {
    let mut result = 0;
    let mut shift = 0;
    let mut position = start_position;
    let mut byte;

    loop {
        byte = data[position];
        position += 1;
        result |= i128::from(byte & 0x7F) << shift;
        shift += 7;

        if (byte & 0x80) == 0 {
            break;
        }
    }

    if (shift < 64) && ((byte & 0x40) != 0) {
        // sign extend
        result |= -(1 << shift);
    }

    (result, position - start_position)
}
<|endoftext|>


<commit_before>extern crate proc_macro;

use proc_macro::*;

#[proc_macro_attribute]
pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
    let span = Span::call_site();
    let mut ret = TokenStream::new();
    ret.extend(Some(TokenTree::from(Punct::new('#', Spacing::Alone))));
    let test = TokenTree::from(Ident::new("test", span));
    ret.extend(Some(TokenTree::from(Group::new(
        Delimiter::Bracket,
        test.into(),
    ))));

    let build_std = contains_ident(&attr, "build_std");

    for token in item {
        let group = match token {
            TokenTree::Group(g) => {
                if g.delimiter() == Delimiter::Brace {
                    g
                } else {
                    ret.extend(Some(TokenTree::Group(g)));
                    continue;
                }
            }
            other => {
                ret.extend(Some(other));
                continue;
            }
        };

        let mut new_body =
            to_token_stream("let _test_guard = cargo_test_support::paths::init_root();");

        // If this is a `build_std` test (aka `tests/build-std/*.rs`) then they
        // only run on nightly and they only run when specifically instructed to
        // on CI.
        if build_std {
            let ts = to_token_stream("if !cargo_test_support::is_nightly() { return }");
            new_body.extend(ts);
            let ts = to_token_stream(
                "if std::env::var(\"CARGO_RUN_BUILD_STD_TESTS\").is_err() { return }",
            );
            new_body.extend(ts);
        }
        new_body.extend(group.stream());
        ret.extend(Some(TokenTree::from(Group::new(
            group.delimiter(),
            new_body,
        ))));
    }

    return ret;
}

fn contains_ident(t: &TokenStream, ident: &str) -> bool {
    t.clone().into_iter().any(|t| match t {
        TokenTree::Ident(i) => i.to_string() == ident,
        _ => false,
    })
}

fn to_token_stream(code: &str) -> TokenStream {
    code.parse().unwrap()
}
<commit_msg>implement clippy suggestion to remove needless `return`<commit_after>extern crate proc_macro;

use proc_macro::*;

#[proc_macro_attribute]
pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
    let span = Span::call_site();
    let mut ret = TokenStream::new();
    ret.extend(Some(TokenTree::from(Punct::new('#', Spacing::Alone))));
    let test = TokenTree::from(Ident::new("test", span));
    ret.extend(Some(TokenTree::from(Group::new(
        Delimiter::Bracket,
        test.into(),
    ))));

    let build_std = contains_ident(&attr, "build_std");

    for token in item {
        let group = match token {
            TokenTree::Group(g) => {
                if g.delimiter() == Delimiter::Brace {
                    g
                } else {
                    ret.extend(Some(TokenTree::Group(g)));
                    continue;
                }
            }
            other => {
                ret.extend(Some(other));
                continue;
            }
        };

        let mut new_body =
            to_token_stream("let _test_guard = cargo_test_support::paths::init_root();");

        // If this is a `build_std` test (aka `tests/build-std/*.rs`) then they
        // only run on nightly and they only run when specifically instructed to
        // on CI.
        if build_std {
            let ts = to_token_stream("if !cargo_test_support::is_nightly() { return }");
            new_body.extend(ts);
            let ts = to_token_stream(
                "if std::env::var(\"CARGO_RUN_BUILD_STD_TESTS\").is_err() { return }",
            );
            new_body.extend(ts);
        }
        new_body.extend(group.stream());
        ret.extend(Some(TokenTree::from(Group::new(
            group.delimiter(),
            new_body,
        ))));
    }

    ret
}

fn contains_ident(t: &TokenStream, ident: &str) -> bool {
    t.clone().into_iter().any(|t| match t {
        TokenTree::Ident(i) => i.to_string() == ident,
        _ => false,
    })
}

fn to_token_stream(code: &str) -> TokenStream {
    code.parse().unwrap()
}
<|endoftext|>


<commit_before>/// Lookup hir elements using positions in the source code. This is a lossy
/// transformation: in general, a single source might correspond to several
/// modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on
/// modules.
///
/// So, this modules should not be used during hir construction, it exists
/// purely for "IDE needs".
use std::sync::Arc;

use ra_db::{FileId, FilePosition};
use ra_syntax::{
    SyntaxNode, AstPtr,
    ast::{self, AstNode, NameOwner},
    algo::find_node_at_offset,
};

use crate::{
    HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody,
    AsName, Module, HirFileId, Crate, Trait, Resolver,
    ids::LocationCtx,
    expr, AstId
};

/// Locates the module by `FileId`. Picks topmost module in the file.
pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Option<Module> {
    module_from_source(db, file_id.into(), None)
}

/// Locates the child module by `mod child;` declaration.
pub fn module_from_declaration(
    db: &impl HirDatabase,
    file_id: FileId,
    decl: &ast::Module,
) -> Option<Module> {
    let parent_module = module_from_file_id(db, file_id);
    let child_name = decl.name();
    match (parent_module, child_name) {
        (Some(parent_module), Some(child_name)) => parent_module.child(db, &child_name.as_name()),
        _ => None,
    }
}

/// Locates the module by position in the source code.
pub fn module_from_position(db: &impl HirDatabase, position: FilePosition) -> Option<Module> {
    let file = db.parse(position.file_id);
    match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) {
        Some(m) if !m.has_semi() => module_from_inline(db, position.file_id.into(), m),
        _ => module_from_file_id(db, position.file_id.into()),
    }
}

fn module_from_inline(
    db: &impl HirDatabase,
    file_id: FileId,
    module: &ast::Module,
) -> Option<Module> {
    assert!(!module.has_semi());
    let file_id = file_id.into();
    let ast_id_map = db.ast_id_map(file_id);
    let item_id = ast_id_map.ast_id(module).with_file_id(file_id);
    module_from_source(db, file_id, Some(item_id))
}

/// Locates the module by child syntax element within the module
pub fn module_from_child_node(
    db: &impl HirDatabase,
    file_id: FileId,
    child: &SyntaxNode,
) -> Option<Module> {
    if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
        module_from_inline(db, file_id.into(), m)
    } else {
        module_from_file_id(db, file_id.into())
    }
}

fn module_from_source(
    db: &impl HirDatabase,
    file_id: HirFileId,
    decl_id: Option<AstId<ast::Module>>,
) -> Option<Module> {
    let source_root_id = db.file_source_root(file_id.as_original_file());
    db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map(
        |krate| {
            let def_map = db.crate_def_map(krate);
            let module_id = def_map.find_module_by_source(file_id, decl_id)?;
            Some(Module { krate, module_id })
        },
    )
}

pub fn const_from_source(
    db: &impl HirDatabase,
    file_id: FileId,
    const_def: &ast::ConstDef,
) -> Option<Const> {
    let module = module_from_child_node(db, file_id, const_def.syntax())?;
    let res = const_from_module(db, module, const_def);
    Some(res)
}

pub fn const_from_module(
    db: &impl HirDatabase,
    module: Module,
    const_def: &ast::ConstDef,
) -> Const {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Const { id: ctx.to_def(const_def) }
}

pub fn function_from_position(db: &impl HirDatabase, position: FilePosition) -> Option<Function> {
    let file = db.parse(position.file_id);
    let fn_def = find_node_at_offset::<ast::FnDef>(file.syntax(), position.offset)?;
    function_from_source(db, position.file_id, fn_def)
}

pub fn function_from_source(
    db: &impl HirDatabase,
    file_id: FileId,
    fn_def: &ast::FnDef,
) -> Option<Function> {
    let module = module_from_child_node(db, file_id, fn_def.syntax())?;
    let res = function_from_module(db, module, fn_def);
    Some(res)
}

pub fn function_from_module(
    db: &impl HirDatabase,
    module: Module,
    fn_def: &ast::FnDef,
) -> Function {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Function { id: ctx.to_def(fn_def) }
}

pub fn function_from_child_node(
    db: &impl HirDatabase,
    file_id: FileId,
    node: &SyntaxNode,
) -> Option<Function> {
    let fn_def = node.ancestors().find_map(ast::FnDef::cast)?;
    function_from_source(db, file_id, fn_def)
}

pub fn struct_from_module(
    db: &impl HirDatabase,
    module: Module,
    struct_def: &ast::StructDef,
) -> Struct {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Struct { id: ctx.to_def(struct_def) }
}

pub fn static_from_source(
    db: &impl HirDatabase,
    file_id: FileId,
    static_def: &ast::StaticDef,
) -> Option<Static> {
    let module = module_from_child_node(db, file_id, static_def.syntax())?;
    let res = static_from_module(db, module, static_def);
    Some(res)
}

pub fn static_from_module(
    db: &impl HirDatabase,
    module: Module,
    static_def: &ast::StaticDef,
) -> Static {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Static { id: ctx.to_def(static_def) }
}

pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::EnumDef) -> Enum {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Enum { id: ctx.to_def(enum_def) }
}

pub fn trait_from_module(
    db: &impl HirDatabase,
    module: Module,
    trait_def: &ast::TraitDef,
) -> Trait {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Trait { id: ctx.to_def(trait_def) }
}

fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> Resolver {
    node.ancestors()
        .find_map(|node| {
            if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() {
                if let Some(func) = function_from_child_node(db, file_id, node) {
                    let scopes = func.scopes(db);
                    let scope = scopes.scope_for(&node);
                    Some(expr::resolver_for_scope(func.body(db), db, scope))
                } else {
                    // FIXME const/static/array length
                    None
                }
            } else {
                try_get_resolver_for_node(db, file_id, node)
            }
        })
        .unwrap_or_default()
}

fn try_get_resolver_for_node(
    db: &impl HirDatabase,
    file_id: FileId,
    node: &SyntaxNode,
) -> Option<Resolver> {
    if let Some(module) = ast::Module::cast(node) {
        Some(module_from_declaration(db, file_id, module)?.resolver(db))
    } else if let Some(_) = ast::SourceFile::cast(node) {
        Some(module_from_source(db, file_id.into(), None)?.resolver(db))
    } else if let Some(s) = ast::StructDef::cast(node) {
        let module = module_from_child_node(db, file_id, s.syntax())?;
        Some(struct_from_module(db, module, s).resolver(db))
    } else if let Some(e) = ast::EnumDef::cast(node) {
        let module = module_from_child_node(db, file_id, e.syntax())?;
        Some(enum_from_module(db, module, e).resolver(db))
    } else if let Some(f) = ast::FnDef::cast(node) {
        function_from_source(db, file_id, f).map(|f| f.resolver(db))
    } else {
        // FIXME add missing cases
        None
    }
}

/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
/// original source files. It should not be used inside the HIR itself.
#[derive(Debug)]
pub struct SourceAnalyzer {
    resolver: Resolver,
    body_source_map: Option<Arc<crate::expr::BodySourceMap>>,
    infer: Option<Arc<crate::ty::InferenceResult>>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathResolution {
    /// An item
    Def(crate::ModuleDef),
    /// A local binding (only value namespace)
    LocalBinding(Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>),
    /// A generic parameter
    GenericParam(u32),
    SelfType(crate::ImplBlock),
    AssocItem(crate::ImplItem),
}

impl SourceAnalyzer {
    pub fn new(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> SourceAnalyzer {
        let def_with_body = node.ancestors().find_map(|node| {
            if let Some(src) = ast::FnDef::cast(node) {
                return function_from_source(db, file_id, src).map(DefWithBody::from);
            }
            if let Some(src) = ast::StaticDef::cast(node) {
                return static_from_source(db, file_id, src).map(DefWithBody::from);
            }
            if let Some(src) = ast::ConstDef::cast(node) {
                return const_from_source(db, file_id, src).map(DefWithBody::from);
            }
            None
        });
        SourceAnalyzer {
            resolver: resolver_for_node(db, file_id, node),
            body_source_map: def_with_body.map(|it| it.body_source_map(db)),
            infer: def_with_body.map(|it| it.infer(db)),
        }
    }

    pub fn resolver(&self) -> &Resolver {
        &self.resolver
    }

    pub fn type_of(&self, _db: &impl HirDatabase, expr: &ast::Expr) -> Option<crate::Ty> {
        let expr_id = self.body_source_map.as_ref()?.node_expr(expr)?;
        Some(self.infer.as_ref()?[expr_id].clone())
    }

    pub fn type_of_pat(&self, _db: &impl HirDatabase, pat: &ast::Pat) -> Option<crate::Ty> {
        let pat_id = self.body_source_map.as_ref()?.node_pat(pat)?;
        Some(self.infer.as_ref()?[pat_id].clone())
    }

    pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
        let expr_id = self.body_source_map.as_ref()?.node_expr(call.into())?;
        self.infer.as_ref()?.method_resolution(expr_id)
    }

    pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> {
        let expr_id = self.body_source_map.as_ref()?.node_expr(field.into())?;
        self.infer.as_ref()?.field_resolution(expr_id)
    }

    pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
        if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
            let expr_id = self.body_source_map.as_ref()?.node_expr(path_expr.into())?;
            if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
                return Some(PathResolution::AssocItem(assoc));
            }
        }
        if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) {
            let pat_id = self.body_source_map.as_ref()?.node_pat(path_pat.into())?;
            if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
                return Some(PathResolution::AssocItem(assoc));
            }
        }
        let hir_path = crate::Path::from_ast(path)?;
        let res = self.resolver.resolve_path(db, &hir_path);
        let res = res.clone().take_types().or_else(|| res.take_values())?;
        let res = match res {
            crate::Resolution::Def(it) => PathResolution::Def(it),
            crate::Resolution::LocalBinding(it) => {
                PathResolution::LocalBinding(self.body_source_map.as_ref()?.pat_syntax(it)?)
            }
            crate::Resolution::GenericParam(it) => PathResolution::GenericParam(it),
            crate::Resolution::SelfType(it) => PathResolution::SelfType(it),
        };
        Some(res)
    }
}
<commit_msg>add todo<commit_after>/// Lookup hir elements using positions in the source code. This is a lossy
/// transformation: in general, a single source might correspond to several
/// modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on
/// modules.
///
/// So, this modules should not be used during hir construction, it exists
/// purely for "IDE needs".
use std::sync::Arc;

use ra_db::{FileId, FilePosition};
use ra_syntax::{
    SyntaxNode, AstPtr,
    ast::{self, AstNode, NameOwner},
    algo::find_node_at_offset,
};

use crate::{
    HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody,
    AsName, Module, HirFileId, Crate, Trait, Resolver,
    ids::LocationCtx,
    expr, AstId
};

/// Locates the module by `FileId`. Picks topmost module in the file.
pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Option<Module> {
    module_from_source(db, file_id.into(), None)
}

/// Locates the child module by `mod child;` declaration.
pub fn module_from_declaration(
    db: &impl HirDatabase,
    file_id: FileId,
    decl: &ast::Module,
) -> Option<Module> {
    let parent_module = module_from_file_id(db, file_id);
    let child_name = decl.name();
    match (parent_module, child_name) {
        (Some(parent_module), Some(child_name)) => parent_module.child(db, &child_name.as_name()),
        _ => None,
    }
}

/// Locates the module by position in the source code.
pub fn module_from_position(db: &impl HirDatabase, position: FilePosition) -> Option<Module> {
    let file = db.parse(position.file_id);
    match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) {
        Some(m) if !m.has_semi() => module_from_inline(db, position.file_id.into(), m),
        _ => module_from_file_id(db, position.file_id.into()),
    }
}

fn module_from_inline(
    db: &impl HirDatabase,
    file_id: FileId,
    module: &ast::Module,
) -> Option<Module> {
    assert!(!module.has_semi());
    let file_id = file_id.into();
    let ast_id_map = db.ast_id_map(file_id);
    let item_id = ast_id_map.ast_id(module).with_file_id(file_id);
    module_from_source(db, file_id, Some(item_id))
}

/// Locates the module by child syntax element within the module
pub fn module_from_child_node(
    db: &impl HirDatabase,
    file_id: FileId,
    child: &SyntaxNode,
) -> Option<Module> {
    if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
        module_from_inline(db, file_id.into(), m)
    } else {
        module_from_file_id(db, file_id.into())
    }
}

fn module_from_source(
    db: &impl HirDatabase,
    file_id: HirFileId,
    decl_id: Option<AstId<ast::Module>>,
) -> Option<Module> {
    let source_root_id = db.file_source_root(file_id.as_original_file());
    db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map(
        |krate| {
            let def_map = db.crate_def_map(krate);
            let module_id = def_map.find_module_by_source(file_id, decl_id)?;
            Some(Module { krate, module_id })
        },
    )
}

pub fn const_from_source(
    db: &impl HirDatabase,
    file_id: FileId,
    const_def: &ast::ConstDef,
) -> Option<Const> {
    let module = module_from_child_node(db, file_id, const_def.syntax())?;
    let res = const_from_module(db, module, const_def);
    Some(res)
}

pub fn const_from_module(
    db: &impl HirDatabase,
    module: Module,
    const_def: &ast::ConstDef,
) -> Const {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Const { id: ctx.to_def(const_def) }
}

pub fn function_from_position(db: &impl HirDatabase, position: FilePosition) -> Option<Function> {
    let file = db.parse(position.file_id);
    let fn_def = find_node_at_offset::<ast::FnDef>(file.syntax(), position.offset)?;
    function_from_source(db, position.file_id, fn_def)
}

pub fn function_from_source(
    db: &impl HirDatabase,
    file_id: FileId,
    fn_def: &ast::FnDef,
) -> Option<Function> {
    let module = module_from_child_node(db, file_id, fn_def.syntax())?;
    let res = function_from_module(db, module, fn_def);
    Some(res)
}

pub fn function_from_module(
    db: &impl HirDatabase,
    module: Module,
    fn_def: &ast::FnDef,
) -> Function {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Function { id: ctx.to_def(fn_def) }
}

pub fn function_from_child_node(
    db: &impl HirDatabase,
    file_id: FileId,
    node: &SyntaxNode,
) -> Option<Function> {
    let fn_def = node.ancestors().find_map(ast::FnDef::cast)?;
    function_from_source(db, file_id, fn_def)
}

pub fn struct_from_module(
    db: &impl HirDatabase,
    module: Module,
    struct_def: &ast::StructDef,
) -> Struct {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Struct { id: ctx.to_def(struct_def) }
}

pub fn static_from_source(
    db: &impl HirDatabase,
    file_id: FileId,
    static_def: &ast::StaticDef,
) -> Option<Static> {
    let module = module_from_child_node(db, file_id, static_def.syntax())?;
    let res = static_from_module(db, module, static_def);
    Some(res)
}

pub fn static_from_module(
    db: &impl HirDatabase,
    module: Module,
    static_def: &ast::StaticDef,
) -> Static {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Static { id: ctx.to_def(static_def) }
}

pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::EnumDef) -> Enum {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Enum { id: ctx.to_def(enum_def) }
}

pub fn trait_from_module(
    db: &impl HirDatabase,
    module: Module,
    trait_def: &ast::TraitDef,
) -> Trait {
    let (file_id, _) = module.definition_source(db);
    let file_id = file_id.into();
    let ctx = LocationCtx::new(db, module, file_id);
    Trait { id: ctx.to_def(trait_def) }
}

fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> Resolver {
    node.ancestors()
        .find_map(|node| {
            if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() {
                if let Some(func) = function_from_child_node(db, file_id, node) {
                    let scopes = func.scopes(db);
                    let scope = scopes.scope_for(&node);
                    Some(expr::resolver_for_scope(func.body(db), db, scope))
                } else {
                    // FIXME const/static/array length
                    None
                }
            } else {
                try_get_resolver_for_node(db, file_id, node)
            }
        })
        .unwrap_or_default()
}

fn try_get_resolver_for_node(
    db: &impl HirDatabase,
    file_id: FileId,
    node: &SyntaxNode,
) -> Option<Resolver> {
    if let Some(module) = ast::Module::cast(node) {
        Some(module_from_declaration(db, file_id, module)?.resolver(db))
    } else if let Some(_) = ast::SourceFile::cast(node) {
        Some(module_from_source(db, file_id.into(), None)?.resolver(db))
    } else if let Some(s) = ast::StructDef::cast(node) {
        let module = module_from_child_node(db, file_id, s.syntax())?;
        Some(struct_from_module(db, module, s).resolver(db))
    } else if let Some(e) = ast::EnumDef::cast(node) {
        let module = module_from_child_node(db, file_id, e.syntax())?;
        Some(enum_from_module(db, module, e).resolver(db))
    } else if let Some(f) = ast::FnDef::cast(node) {
        function_from_source(db, file_id, f).map(|f| f.resolver(db))
    } else {
        // FIXME add missing cases
        None
    }
}

/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
/// original source files. It should not be used inside the HIR itself.
#[derive(Debug)]
pub struct SourceAnalyzer {
    resolver: Resolver,
    body_source_map: Option<Arc<crate::expr::BodySourceMap>>,
    infer: Option<Arc<crate::ty::InferenceResult>>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathResolution {
    /// An item
    Def(crate::ModuleDef),
    /// A local binding (only value namespace)
    LocalBinding(Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>),
    /// A generic parameter
    GenericParam(u32),
    SelfType(crate::ImplBlock),
    AssocItem(crate::ImplItem),
}

impl SourceAnalyzer {
    pub fn new(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> SourceAnalyzer {
        let def_with_body = node.ancestors().find_map(|node| {
            if let Some(src) = ast::FnDef::cast(node) {
                return function_from_source(db, file_id, src).map(DefWithBody::from);
            }
            if let Some(src) = ast::StaticDef::cast(node) {
                return static_from_source(db, file_id, src).map(DefWithBody::from);
            }
            if let Some(src) = ast::ConstDef::cast(node) {
                return const_from_source(db, file_id, src).map(DefWithBody::from);
            }
            None
        });
        SourceAnalyzer {
            //TODO: use scope_for_offset here to get correct scope for completion
            resolver: resolver_for_node(db, file_id, node),
            body_source_map: def_with_body.map(|it| it.body_source_map(db)),
            infer: def_with_body.map(|it| it.infer(db)),
        }
    }

    pub fn resolver(&self) -> &Resolver {
        &self.resolver
    }

    pub fn type_of(&self, _db: &impl HirDatabase, expr: &ast::Expr) -> Option<crate::Ty> {
        let expr_id = self.body_source_map.as_ref()?.node_expr(expr)?;
        Some(self.infer.as_ref()?[expr_id].clone())
    }

    pub fn type_of_pat(&self, _db: &impl HirDatabase, pat: &ast::Pat) -> Option<crate::Ty> {
        let pat_id = self.body_source_map.as_ref()?.node_pat(pat)?;
        Some(self.infer.as_ref()?[pat_id].clone())
    }

    pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
        let expr_id = self.body_source_map.as_ref()?.node_expr(call.into())?;
        self.infer.as_ref()?.method_resolution(expr_id)
    }

    pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> {
        let expr_id = self.body_source_map.as_ref()?.node_expr(field.into())?;
        self.infer.as_ref()?.field_resolution(expr_id)
    }

    pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
        if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
            let expr_id = self.body_source_map.as_ref()?.node_expr(path_expr.into())?;
            if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
                return Some(PathResolution::AssocItem(assoc));
            }
        }
        if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) {
            let pat_id = self.body_source_map.as_ref()?.node_pat(path_pat.into())?;
            if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
                return Some(PathResolution::AssocItem(assoc));
            }
        }
        let hir_path = crate::Path::from_ast(path)?;
        let res = self.resolver.resolve_path(db, &hir_path);
        let res = res.clone().take_types().or_else(|| res.take_values())?;
        let res = match res {
            crate::Resolution::Def(it) => PathResolution::Def(it),
            crate::Resolution::LocalBinding(it) => {
                PathResolution::LocalBinding(self.body_source_map.as_ref()?.pat_syntax(it)?)
            }
            crate::Resolution::GenericParam(it) => PathResolution::GenericParam(it),
            crate::Resolution::SelfType(it) => PathResolution::SelfType(it),
        };
        Some(res)
    }
}
<|endoftext|>


<commit_before>//! Wrapper for an OpenGL buffer object.

use std::mem;
use gl;
use gl::types::*;
use resource::gl_primitive::GLPrimitive;

#[path = "../error.rs"]
mod error;

struct GLHandle {
    handle: GLuint
}

impl GLHandle {
    pub fn new(handle: GLuint) -> GLHandle {
        GLHandle {
            handle: handle
        }
    }

    pub fn handle(&self) -> GLuint {
        self.handle
    }
}

impl Drop for GLHandle {
    fn drop(&mut self) {
        unsafe {
            verify!(gl::DeleteBuffers(1, &self.handle))
        }
    }
}

// FIXME: generalize this for any resource: GPUResource
/// A vector of elements that can be loaded to the GPU, on the RAM, or both.
pub struct GPUVec<T> {
    trash:      bool,
    len:        usize,
    buf_type:   BufferType,
    alloc_type: AllocationType,
    handle:     Option<(usize, GLHandle)>,
    data:       Option<Vec<T>>,
}

// FIXME: implement Clone

impl<T: GLPrimitive> GPUVec<T> {
    /// Creates a new `GPUVec` that is not yet uploaded to the GPU.
    pub fn new(data: Vec<T>, buf_type: BufferType, alloc_type: AllocationType) -> GPUVec<T> {
        GPUVec {
            trash:      true,
            len:        data.len(),
            buf_type:   buf_type,
            alloc_type: alloc_type,
            handle:     None,
            data:       Some(data)
        }
    }

    /// The length of this vector.
    #[inline]
    pub fn len(&self) -> usize {
        if self.trash {
            match self.data {
                Some(ref d) => d.len(),
                None        => panic!("This should never happend.")

            }
        }
        else {
            self.len
        }
    }

    /// Mutably accesses the vector if it is available on RAM.
    ///
    /// This method will mark this vector as `trash`.
    #[inline]
    pub fn data_mut(&mut self) -> &mut Option<Vec<T>> {
        self.trash = true;

        &mut self.data
    }

    /// Immutably accesses the vector if it is available on RAM.
    #[inline]
    pub fn data(&self) -> &Option<Vec<T>> {
        &self.data
    }

    /// Returns `true` if this vector is already uploaded to the GPU.
    #[inline]
    pub fn is_on_gpu(&self) -> bool {
        self.handle.is_some()
    }

    /// Returns `true` if the cpu data and gpu data are out of sync.
    #[inline]
    pub fn trash(&self) -> bool {
        self.trash
    }

    /// Returns `true` if this vector is available on RAM.
    ///
    /// Note that a `GPUVec` may be both on RAM and on the GPU.
    #[inline]
    pub fn is_on_ram(&self) -> bool {
        self.data.is_some()
    }

    /// Loads the vector from the RAM to the GPU.
    ///
    /// If the vector is not available on RAM or already loaded to the GPU, nothing will happen.
    #[inline]
    pub fn load_to_gpu(&mut self) {
        if !self.is_on_gpu() {
            let buf_type   = self.buf_type;
            let alloc_type = self.alloc_type;
            let len        = &mut self.len;

            self.handle = self.data.as_ref().map(|d| {
                *len = d.len();
                (d.len(), GLHandle::new(upload_buffer(&d[..], buf_type, alloc_type)))
            });
        }
        else if self.trash() {
            for d in self.data.iter() {
                self.len = d.len();

                match self.handle {
                    None => { },
                    Some((ref mut len, ref handle)) => {
                        let handle = handle.handle();

                        *len = update_buffer(&d[..], *len, handle, self.buf_type, self.alloc_type)
                    }
                }
            }
        }

        self.trash = false;
    }

    /// Binds this vector to the appropriate gpu array.
    ///
    /// This does not associate this buffer with any shader attribute.
    #[inline]
    pub fn bind(&mut self) {
        self.load_to_gpu();

        let handle = self.handle.as_ref().map(|&(_, ref h)| h.handle()).expect("Could not bind the vector: data unavailable.");
        verify!(gl::BindBuffer(self.buf_type.to_gl(), handle));
    }

    /// Unbind this vector to the corresponding gpu buffer.
    #[inline]
    pub fn unbind(&mut self) {
        if self.is_on_gpu() {
            verify!(gl::BindBuffer(self.buf_type.to_gl(), 0));
        }
    }

    /// Loads the vector from the GPU to the RAM.
    ///
    /// If the vector is not available on the GPU or already loaded to the RAM, nothing will
    /// happen.
    #[inline]
    pub fn load_to_ram(&mut self) {
        if !self.is_on_ram() && self.is_on_gpu() {
            assert!(!self.trash);
            let     handle = self.handle.as_ref().map(|&(_, ref h)| h.handle()).unwrap();
            let mut data   = Vec::with_capacity(self.len);

            unsafe { data.set_len(self.len) };
            download_buffer(handle, self.buf_type, &mut data[..]);
            self.data = Some(data);
        }
    }

    /// Unloads this resource from the GPU.
    #[inline]
    pub fn unload_from_gpu(&mut self) {
        let _ = self.handle.as_ref().map(|&(_, ref h)| unsafe { verify!(gl::DeleteBuffers(1, &h.handle())) });
        self.len    = self.len();
        self.handle = None;
        self.trash  = false;
    }

    /// Removes this resource from the RAM.
    ///
    /// This is useful to save memory for vectors required on the GPU only.
    #[inline]
    pub fn unload_from_ram(&mut self) {
        if self.trash && self.is_on_gpu() {
            self.load_to_gpu();
        }

        self.data = None;
    }
}

impl<T: Clone + GLPrimitive> GPUVec<T> {
    /// Returns this vector as an owned vector if it is available on RAM.
    ///
    /// If it has been uploaded to the GPU, and unloaded from the RAM, call `load_to_ram` first to
    /// make the data accessible.
    #[inline]
    pub fn to_owned(&self) -> Option<Vec<T>> {
        self.data.as_ref().map(|d| d.clone())
    }
}

/// Type of gpu buffer.
#[derive(Clone, Copy)]
pub enum BufferType {
    /// An array buffer bindable to a gl::ARRAY_BUFFER.
    Array,
    /// An array buffer bindable to a gl::ELEMENT_ARRAY_BUFFER.
    ElementArray
}

impl BufferType {
    #[inline]
    fn to_gl(&self) -> GLuint {
        match *self {
            BufferType::Array        => gl::ARRAY_BUFFER,
            BufferType::ElementArray => gl::ELEMENT_ARRAY_BUFFER
        }
    }
}

/// Allocation type of gpu buffers.
#[derive(Clone, Copy)]
pub enum AllocationType {
    /// STATIC_DRAW allocation type.
    StaticDraw,
    /// DYNAMIC_DRAW allocation type.
    DynamicDraw,
    /// STREAM_DRAW allocation type.
    StreamDraw
}

impl AllocationType {
    #[inline]
    fn to_gl(&self) -> GLuint {
        match *self {
            AllocationType::StaticDraw  => gl::STATIC_DRAW,
            AllocationType::DynamicDraw => gl::DYNAMIC_DRAW,
            AllocationType::StreamDraw  => gl::STREAM_DRAW
        }
    }
}

/// Allocates and uploads a buffer to the gpu.
#[inline]
pub fn upload_buffer<T: GLPrimitive>(buf:             &[T],
                                     buf_type:        BufferType,
                                     allocation_type: AllocationType)
                                     -> GLuint {
    // Upload values of vertices
    let mut buf_id: GLuint = 0;

    unsafe {
        verify!(gl::GenBuffers(1, &mut buf_id));
        let _ = update_buffer(buf, 0, buf_id, buf_type, allocation_type);
    }

    buf_id
}

/// Downloads a buffer from the gpu.
///
///
#[inline]
pub fn download_buffer<T: GLPrimitive>(buf_id: GLuint, buf_type: BufferType, out: &mut [T]) {
    unsafe {
        verify!(gl::BindBuffer(buf_type.to_gl(), buf_id));
        verify!(gl::GetBufferSubData(
                buf_type.to_gl(),
                0,
                (out.len() * mem::size_of::<T>()) as GLsizeiptr,
                mem::transmute(&out[0])));
    }
}

/// Updates a buffer to the gpu.
///
/// Returns the number of element the bufer on the gpu can hold.
#[inline]
pub fn update_buffer<T: GLPrimitive>(buf:                 &[T],
                                     gpu_buf_len:         usize,
                                     gpu_buf_id:          GLuint,
                                     gpu_buf_type:        BufferType,
                                     gpu_allocation_type: AllocationType)
                                     -> usize {
    unsafe {
        verify!(gl::BindBuffer(gpu_buf_type.to_gl(), gpu_buf_id));

        if buf.len() < gpu_buf_len {
            verify!(gl::BufferSubData(
                    gpu_buf_type.to_gl(),
                    0,
                    (buf.len() * mem::size_of::<T>()) as GLsizeiptr,
                    mem::transmute(&buf[0])));

            gpu_buf_len
        }
        else {
            verify!(gl::BufferData(
                    gpu_buf_type.to_gl(),
                    (buf.len() * mem::size_of::<T>()) as GLsizeiptr,
                    mem::transmute(&buf[0]),
                    gpu_allocation_type.to_gl()));

            buf.len()
        }
    }
}
<commit_msg>Another guard to prevent GL_INVALID_OPERATION errors on exit<commit_after>//! Wrapper for an OpenGL buffer object.

use std::mem;
use gl;
use gl::types::*;
use resource::gl_primitive::GLPrimitive;

#[path = "../error.rs"]
mod error;

struct GLHandle {
    handle: GLuint
}

impl GLHandle {
    pub fn new(handle: GLuint) -> GLHandle {
        GLHandle {
            handle: handle
        }
    }

    pub fn handle(&self) -> GLuint {
        self.handle
    }
}

impl Drop for GLHandle {
    fn drop(&mut self) {
        unsafe {
            if gl::IsBuffer(self.handle) != 0 {
                verify!(gl::DeleteBuffers(1, &self.handle))
            }
        }
    }
}

// FIXME: generalize this for any resource: GPUResource
/// A vector of elements that can be loaded to the GPU, on the RAM, or both.
pub struct GPUVec<T> {
    trash:      bool,
    len:        usize,
    buf_type:   BufferType,
    alloc_type: AllocationType,
    handle:     Option<(usize, GLHandle)>,
    data:       Option<Vec<T>>,
}

// FIXME: implement Clone

impl<T: GLPrimitive> GPUVec<T> {
    /// Creates a new `GPUVec` that is not yet uploaded to the GPU.
    pub fn new(data: Vec<T>, buf_type: BufferType, alloc_type: AllocationType) -> GPUVec<T> {
        GPUVec {
            trash:      true,
            len:        data.len(),
            buf_type:   buf_type,
            alloc_type: alloc_type,
            handle:     None,
            data:       Some(data)
        }
    }

    /// The length of this vector.
    #[inline]
    pub fn len(&self) -> usize {
        if self.trash {
            match self.data {
                Some(ref d) => d.len(),
                None        => panic!("This should never happend.")

            }
        }
        else {
            self.len
        }
    }

    /// Mutably accesses the vector if it is available on RAM.
    ///
    /// This method will mark this vector as `trash`.
    #[inline]
    pub fn data_mut(&mut self) -> &mut Option<Vec<T>> {
        self.trash = true;

        &mut self.data
    }

    /// Immutably accesses the vector if it is available on RAM.
    #[inline]
    pub fn data(&self) -> &Option<Vec<T>> {
        &self.data
    }

    /// Returns `true` if this vector is already uploaded to the GPU.
    #[inline]
    pub fn is_on_gpu(&self) -> bool {
        self.handle.is_some()
    }

    /// Returns `true` if the cpu data and gpu data are out of sync.
    #[inline]
    pub fn trash(&self) -> bool {
        self.trash
    }

    /// Returns `true` if this vector is available on RAM.
    ///
    /// Note that a `GPUVec` may be both on RAM and on the GPU.
    #[inline]
    pub fn is_on_ram(&self) -> bool {
        self.data.is_some()
    }

    /// Loads the vector from the RAM to the GPU.
    ///
    /// If the vector is not available on RAM or already loaded to the GPU, nothing will happen.
    #[inline]
    pub fn load_to_gpu(&mut self) {
        if !self.is_on_gpu() {
            let buf_type   = self.buf_type;
            let alloc_type = self.alloc_type;
            let len        = &mut self.len;

            self.handle = self.data.as_ref().map(|d| {
                *len = d.len();
                (d.len(), GLHandle::new(upload_buffer(&d[..], buf_type, alloc_type)))
            });
        }
        else if self.trash() {
            for d in self.data.iter() {
                self.len = d.len();

                match self.handle {
                    None => { },
                    Some((ref mut len, ref handle)) => {
                        let handle = handle.handle();

                        *len = update_buffer(&d[..], *len, handle, self.buf_type, self.alloc_type)
                    }
                }
            }
        }

        self.trash = false;
    }

    /// Binds this vector to the appropriate gpu array.
    ///
    /// This does not associate this buffer with any shader attribute.
    #[inline]
    pub fn bind(&mut self) {
        self.load_to_gpu();

        let handle = self.handle.as_ref().map(|&(_, ref h)| h.handle()).expect("Could not bind the vector: data unavailable.");
        verify!(gl::BindBuffer(self.buf_type.to_gl(), handle));
    }

    /// Unbind this vector to the corresponding gpu buffer.
    #[inline]
    pub fn unbind(&mut self) {
        if self.is_on_gpu() {
            verify!(gl::BindBuffer(self.buf_type.to_gl(), 0));
        }
    }

    /// Loads the vector from the GPU to the RAM.
    ///
    /// If the vector is not available on the GPU or already loaded to the RAM, nothing will
    /// happen.
    #[inline]
    pub fn load_to_ram(&mut self) {
        if !self.is_on_ram() && self.is_on_gpu() {
            assert!(!self.trash);
            let     handle = self.handle.as_ref().map(|&(_, ref h)| h.handle()).unwrap();
            let mut data   = Vec::with_capacity(self.len);

            unsafe { data.set_len(self.len) };
            download_buffer(handle, self.buf_type, &mut data[..]);
            self.data = Some(data);
        }
    }

    /// Unloads this resource from the GPU.
    #[inline]
    pub fn unload_from_gpu(&mut self) {
        let _ = self.handle.as_ref().map(|&(_, ref h)| unsafe { verify!(gl::DeleteBuffers(1, &h.handle())) });
        self.len    = self.len();
        self.handle = None;
        self.trash  = false;
    }

    /// Removes this resource from the RAM.
    ///
    /// This is useful to save memory for vectors required on the GPU only.
    #[inline]
    pub fn unload_from_ram(&mut self) {
        if self.trash && self.is_on_gpu() {
            self.load_to_gpu();
        }

        self.data = None;
    }
}

impl<T: Clone + GLPrimitive> GPUVec<T> {
    /// Returns this vector as an owned vector if it is available on RAM.
    ///
    /// If it has been uploaded to the GPU, and unloaded from the RAM, call `load_to_ram` first to
    /// make the data accessible.
    #[inline]
    pub fn to_owned(&self) -> Option<Vec<T>> {
        self.data.as_ref().map(|d| d.clone())
    }
}

/// Type of gpu buffer.
#[derive(Clone, Copy)]
pub enum BufferType {
    /// An array buffer bindable to a gl::ARRAY_BUFFER.
    Array,
    /// An array buffer bindable to a gl::ELEMENT_ARRAY_BUFFER.
    ElementArray
}

impl BufferType {
    #[inline]
    fn to_gl(&self) -> GLuint {
        match *self {
            BufferType::Array        => gl::ARRAY_BUFFER,
            BufferType::ElementArray => gl::ELEMENT_ARRAY_BUFFER
        }
    }
}

/// Allocation type of gpu buffers.
#[derive(Clone, Copy)]
pub enum AllocationType {
    /// STATIC_DRAW allocation type.
    StaticDraw,
    /// DYNAMIC_DRAW allocation type.
    DynamicDraw,
    /// STREAM_DRAW allocation type.
    StreamDraw
}

impl AllocationType {
    #[inline]
    fn to_gl(&self) -> GLuint {
        match *self {
            AllocationType::StaticDraw  => gl::STATIC_DRAW,
            AllocationType::DynamicDraw => gl::DYNAMIC_DRAW,
            AllocationType::StreamDraw  => gl::STREAM_DRAW
        }
    }
}

/// Allocates and uploads a buffer to the gpu.
#[inline]
pub fn upload_buffer<T: GLPrimitive>(buf:             &[T],
                                     buf_type:        BufferType,
                                     allocation_type: AllocationType)
                                     -> GLuint {
    // Upload values of vertices
    let mut buf_id: GLuint = 0;

    unsafe {
        verify!(gl::GenBuffers(1, &mut buf_id));
        let _ = update_buffer(buf, 0, buf_id, buf_type, allocation_type);
    }

    buf_id
}

/// Downloads a buffer from the gpu.
///
///
#[inline]
pub fn download_buffer<T: GLPrimitive>(buf_id: GLuint, buf_type: BufferType, out: &mut [T]) {
    unsafe {
        verify!(gl::BindBuffer(buf_type.to_gl(), buf_id));
        verify!(gl::GetBufferSubData(
                buf_type.to_gl(),
                0,
                (out.len() * mem::size_of::<T>()) as GLsizeiptr,
                mem::transmute(&out[0])));
    }
}

/// Updates a buffer to the gpu.
///
/// Returns the number of element the bufer on the gpu can hold.
#[inline]
pub fn update_buffer<T: GLPrimitive>(buf:                 &[T],
                                     gpu_buf_len:         usize,
                                     gpu_buf_id:          GLuint,
                                     gpu_buf_type:        BufferType,
                                     gpu_allocation_type: AllocationType)
                                     -> usize {
    unsafe {
        verify!(gl::BindBuffer(gpu_buf_type.to_gl(), gpu_buf_id));

        if buf.len() < gpu_buf_len {
            verify!(gl::BufferSubData(
                    gpu_buf_type.to_gl(),
                    0,
                    (buf.len() * mem::size_of::<T>()) as GLsizeiptr,
                    mem::transmute(&buf[0])));

            gpu_buf_len
        }
        else {
            verify!(gl::BufferData(
                    gpu_buf_type.to_gl(),
                    (buf.len() * mem::size_of::<T>()) as GLsizeiptr,
                    mem::transmute(&buf[0]),
                    gpu_allocation_type.to_gl()));

            buf.len()
        }
    }
}
<|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 dom::attr::Attr;
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElementMethods;
use dom::bindings::codegen::InheritTypes::HTMLCanvasElementDerived;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{MutNullableJS, JSRef, Temporary, OptionalSettable};
use dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
use dom::document::Document;
use dom::element::{Element, ElementTypeId, AttributeHandlers};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;

use servo_util::str::{DOMString, parse_unsigned_integer};

use geom::size::Size2D;

use std::cell::Cell;
use std::default::Default;

const DEFAULT_WIDTH: u32 = 300;
const DEFAULT_HEIGHT: u32 = 150;

#[dom_struct]
pub struct HTMLCanvasElement {
    htmlelement: HTMLElement,
    context: MutNullableJS<CanvasRenderingContext2D>,
    width: Cell<u32>,
    height: Cell<u32>,
}

impl HTMLCanvasElementDerived for EventTarget {
    fn is_htmlcanvaselement(&self) -> bool {
        *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLCanvasElement))
    }
}

impl HTMLCanvasElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLCanvasElement {
        HTMLCanvasElement {
            htmlelement: HTMLElement::new_inherited(ElementTypeId::HTMLCanvasElement, localName, prefix, document),
            context: Default::default(),
            width: Cell::new(DEFAULT_WIDTH),
            height: Cell::new(DEFAULT_HEIGHT),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLCanvasElement> {
        let element = HTMLCanvasElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLCanvasElementBinding::Wrap)
    }
}

impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> {
    fn Width(self) -> u32 {
        self.width.get()
    }

    fn SetWidth(self, width: u32) {
        let elem: JSRef<Element> = ElementCast::from_ref(self);
        elem.set_uint_attribute(&atom!("width"), width)
    }

    fn Height(self) -> u32 {
        self.height.get()
    }

    fn SetHeight(self, height: u32) {
        let elem: JSRef<Element> = ElementCast::from_ref(self);
        elem.set_uint_attribute(&atom!("height"), height)
    }

    fn GetContext(self, id: DOMString) -> Option<Temporary<CanvasRenderingContext2D>> {
        if id.as_slice() != "2d" {
            return None;
        }

        if self.context.get().is_none() {
            let window = window_from_node(self).root();
            let (w, h) = (self.width.get() as i32, self.height.get() as i32);
            let context = CanvasRenderingContext2D::new(&GlobalRef::Window(*window), self, Size2D(w, h));
            self.context.assign(Some(context));
        }
        self.context.get()
     }
}

impl<'a> VirtualMethods for JSRef<'a, HTMLCanvasElement> {
    fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
        let element: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
        Some(element as &VirtualMethods)
    }

    fn before_remove_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.before_remove_attr(attr),
            _ => ()
        }

        let recreate = match attr.local_name() {
            &atom!("width") => {
                self.width.set(DEFAULT_WIDTH);
                true
            }
            &atom!("height") => {
                self.height.set(DEFAULT_HEIGHT);
                true
            }
            _ => false,
        };

        if recreate {
            let (w, h) = (self.width.get() as i32, self.height.get() as i32);
            match self.context.get() {
                Some(ref context) => context.root().recreate(Size2D(w, h)),
                None => ()
            }
        }
    }

    fn after_set_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(attr),
            _ => ()
        }

        let value = attr.value();
        let recreate = match attr.local_name() {
            &atom!("width") => {
                self.width.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DEFAULT_WIDTH));
                true
            }
            &atom!("height") => {
                self.height.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DEFAULT_HEIGHT));
                true
            }
            _ => false,
        };

        if recreate {
            let (w, h) = (self.width.get() as i32, self.height.get() as i32);
            match self.context.get() {
                Some(ref context) => context.root().recreate(Size2D(w, h)),
                None => ()
            }
        }
    }
}

<commit_msg>auto merge of #4520 : Ms2ger/servo/or_init, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::Attr;
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElementMethods;
use dom::bindings::codegen::InheritTypes::HTMLCanvasElementDerived;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{MutNullableJS, JSRef, Temporary};
use dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
use dom::document::Document;
use dom::element::{Element, ElementTypeId, AttributeHandlers};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;

use servo_util::str::{DOMString, parse_unsigned_integer};

use geom::size::Size2D;

use std::cell::Cell;
use std::default::Default;

const DEFAULT_WIDTH: u32 = 300;
const DEFAULT_HEIGHT: u32 = 150;

#[dom_struct]
pub struct HTMLCanvasElement {
    htmlelement: HTMLElement,
    context: MutNullableJS<CanvasRenderingContext2D>,
    width: Cell<u32>,
    height: Cell<u32>,
}

impl HTMLCanvasElementDerived for EventTarget {
    fn is_htmlcanvaselement(&self) -> bool {
        *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLCanvasElement))
    }
}

impl HTMLCanvasElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLCanvasElement {
        HTMLCanvasElement {
            htmlelement: HTMLElement::new_inherited(ElementTypeId::HTMLCanvasElement, localName, prefix, document),
            context: Default::default(),
            width: Cell::new(DEFAULT_WIDTH),
            height: Cell::new(DEFAULT_HEIGHT),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLCanvasElement> {
        let element = HTMLCanvasElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLCanvasElementBinding::Wrap)
    }
}

impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> {
    fn Width(self) -> u32 {
        self.width.get()
    }

    fn SetWidth(self, width: u32) {
        let elem: JSRef<Element> = ElementCast::from_ref(self);
        elem.set_uint_attribute(&atom!("width"), width)
    }

    fn Height(self) -> u32 {
        self.height.get()
    }

    fn SetHeight(self, height: u32) {
        let elem: JSRef<Element> = ElementCast::from_ref(self);
        elem.set_uint_attribute(&atom!("height"), height)
    }

    fn GetContext(self, id: DOMString) -> Option<Temporary<CanvasRenderingContext2D>> {
        if id.as_slice() != "2d" {
            return None;
        }

        Some(self.context.or_init(|| {
            let window = window_from_node(self).root();
            let (w, h) = (self.width.get() as i32, self.height.get() as i32);
            CanvasRenderingContext2D::new(&GlobalRef::Window(*window), self, Size2D(w, h))
        }))
     }
}

impl<'a> VirtualMethods for JSRef<'a, HTMLCanvasElement> {
    fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
        let element: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
        Some(element as &VirtualMethods)
    }

    fn before_remove_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.before_remove_attr(attr),
            _ => ()
        }

        let recreate = match attr.local_name() {
            &atom!("width") => {
                self.width.set(DEFAULT_WIDTH);
                true
            }
            &atom!("height") => {
                self.height.set(DEFAULT_HEIGHT);
                true
            }
            _ => false,
        };

        if recreate {
            let (w, h) = (self.width.get() as i32, self.height.get() as i32);
            match self.context.get() {
                Some(ref context) => context.root().recreate(Size2D(w, h)),
                None => ()
            }
        }
    }

    fn after_set_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(attr),
            _ => ()
        }

        let value = attr.value();
        let recreate = match attr.local_name() {
            &atom!("width") => {
                self.width.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DEFAULT_WIDTH));
                true
            }
            &atom!("height") => {
                self.height.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DEFAULT_HEIGHT));
                true
            }
            _ => false,
        };

        if recreate {
            let (w, h) = (self.width.get() as i32, self.height.get() as i32);
            match self.context.get() {
                Some(ref context) => context.root().recreate(Size2D(w, h)),
                None => ()
            }
        }
    }
}

<|endoftext|>


<commit_before>//! See `AssistContext`

use std::mem;

use algo::find_covering_element;
use hir::Semantics;
use ide_db::base_db::{FileId, FileRange};
use ide_db::{
    label::Label,
    source_change::{SourceChange, SourceFileEdit},
    RootDatabase,
};
use syntax::{
    algo::{self, find_node_at_offset, SyntaxRewriter},
    AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange, TextSize,
    TokenAtOffset,
};
use text_edit::{TextEdit, TextEditBuilder};

use crate::{
    assist_config::{AssistConfig, SnippetCap},
    Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist,
};

/// `AssistContext` allows to apply an assist or check if it could be applied.
///
/// Assists use a somewhat over-engineered approach, given the current needs.
/// The assists workflow consists of two phases. In the first phase, a user asks
/// for the list of available assists. In the second phase, the user picks a
/// particular assist and it gets applied.
///
/// There are two peculiarities here:
///
/// * first, we ideally avoid computing more things then necessary to answer "is
///   assist applicable" in the first phase.
/// * second, when we are applying assist, we don't have a guarantee that there
///   weren't any changes between the point when user asked for assists and when
///   they applied a particular assist. So, when applying assist, we need to do
///   all the checks from scratch.
///
/// To avoid repeating the same code twice for both "check" and "apply"
/// functions, we use an approach reminiscent of that of Django's function based
/// views dealing with forms. Each assist receives a runtime parameter,
/// `resolve`. It first check if an edit is applicable (potentially computing
/// info required to compute the actual edit). If it is applicable, and
/// `resolve` is `true`, it then computes the actual edit.
///
/// So, to implement the original assists workflow, we can first apply each edit
/// with `resolve = false`, and then applying the selected edit again, with
/// `resolve = true` this time.
///
/// Note, however, that we don't actually use such two-phase logic at the
/// moment, because the LSP API is pretty awkward in this place, and it's much
/// easier to just compute the edit eagerly :-)
pub(crate) struct AssistContext<'a> {
    pub(crate) config: &'a AssistConfig,
    pub(crate) sema: Semantics<'a, RootDatabase>,
    pub(crate) frange: FileRange,
    source_file: SourceFile,
}

impl<'a> AssistContext<'a> {
    pub(crate) fn new(
        sema: Semantics<'a, RootDatabase>,
        config: &'a AssistConfig,
        frange: FileRange,
    ) -> AssistContext<'a> {
        let source_file = sema.parse(frange.file_id);
        AssistContext { config, sema, frange, source_file }
    }

    pub(crate) fn db(&self) -> &RootDatabase {
        self.sema.db
    }

    // NB, this ignores active selection.
    pub(crate) fn offset(&self) -> TextSize {
        self.frange.range.start()
    }

    pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
        self.source_file.syntax().token_at_offset(self.offset())
    }
    pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
        self.token_at_offset().find(|it| it.kind() == kind)
    }
    pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
        find_node_at_offset(self.source_file.syntax(), self.offset())
    }
    pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
        self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset())
    }
    pub(crate) fn covering_element(&self) -> SyntaxElement {
        find_covering_element(self.source_file.syntax(), self.frange.range)
    }
    // FIXME: remove
    pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
        find_covering_element(self.source_file.syntax(), range)
    }
}

pub(crate) struct Assists {
    resolve: bool,
    file: FileId,
    buf: Vec<(Assist, Option<SourceChange>)>,
    allowed: Option<Vec<AssistKind>>,
}

impl Assists {
    pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
        Assists {
            resolve: true,
            file: ctx.frange.file_id,
            buf: Vec::new(),
            allowed: ctx.config.allowed.clone(),
        }
    }

    pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
        Assists {
            resolve: false,
            file: ctx.frange.file_id,
            buf: Vec::new(),
            allowed: ctx.config.allowed.clone(),
        }
    }

    pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
        assert!(!self.resolve);
        self.finish()
            .into_iter()
            .map(|(label, edit)| {
                assert!(edit.is_none());
                label
            })
            .collect()
    }

    pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> {
        assert!(self.resolve);
        self.finish()
            .into_iter()
            .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() })
            .collect()
    }

    pub(crate) fn add(
        &mut self,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistBuilder),
    ) -> Option<()> {
        if !self.is_allowed(&id) {
            return None;
        }
        let label = Label::new(label.into());
        let assist = Assist { id, label, group: None, target };
        self.add_impl(assist, f)
    }

    pub(crate) fn add_group(
        &mut self,
        group: &GroupLabel,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistBuilder),
    ) -> Option<()> {
        if !self.is_allowed(&id) {
            return None;
        }
        let label = Label::new(label.into());
        let assist = Assist { id, label, group: Some(group.clone()), target };
        self.add_impl(assist, f)
    }

    fn add_impl(&mut self, assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
        let source_change = if self.resolve {
            let mut builder = AssistBuilder::new(self.file);
            f(&mut builder);
            Some(builder.finish())
        } else {
            None
        };

        self.buf.push((assist, source_change));
        Some(())
    }

    fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> {
        self.buf.sort_by_key(|(label, _edit)| label.target.len());
        self.buf
    }

    fn is_allowed(&self, id: &AssistId) -> bool {
        match &self.allowed {
            Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)),
            None => true,
        }
    }
}

pub(crate) struct AssistBuilder {
    edit: TextEditBuilder,
    file_id: FileId,
    is_snippet: bool,
    change: SourceChange,
}

impl AssistBuilder {
    pub(crate) fn new(file_id: FileId) -> AssistBuilder {
        AssistBuilder {
            edit: TextEdit::builder(),
            file_id,
            is_snippet: false,
            change: SourceChange::default(),
        }
    }

    pub(crate) fn edit_file(&mut self, file_id: FileId) {
        self.file_id = file_id;
    }

    fn commit(&mut self) {
        let edit = mem::take(&mut self.edit).finish();
        if !edit.is_empty() {
            let new_edit = SourceFileEdit { file_id: self.file_id, edit };
            assert!(!self.change.source_file_edits.iter().any(|it| it.file_id == new_edit.file_id));
            self.change.source_file_edits.push(new_edit);
        }
    }

    /// Remove specified `range` of text.
    pub(crate) fn delete(&mut self, range: TextRange) {
        self.edit.delete(range)
    }
    /// Append specified `text` at the given `offset`
    pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
        self.edit.insert(offset, text.into())
    }
    /// Append specified `snippet` at the given `offset`
    pub(crate) fn insert_snippet(
        &mut self,
        _cap: SnippetCap,
        offset: TextSize,
        snippet: impl Into<String>,
    ) {
        self.is_snippet = true;
        self.insert(offset, snippet);
    }
    /// Replaces specified `range` of text with a given string.
    pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
        self.edit.replace(range, replace_with.into())
    }
    /// Replaces specified `range` of text with a given `snippet`.
    pub(crate) fn replace_snippet(
        &mut self,
        _cap: SnippetCap,
        range: TextRange,
        snippet: impl Into<String>,
    ) {
        self.is_snippet = true;
        self.replace(range, snippet);
    }
    pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
        algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
    }
    pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
        let node = rewriter.rewrite_root().unwrap();
        let new = rewriter.rewrite(&node);
        algo::diff(&node, &new).into_text_edit(&mut self.edit);
    }

    // FIXME: kill this API
    /// Get access to the raw `TextEditBuilder`.
    pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
        &mut self.edit
    }

    fn finish(mut self) -> SourceChange {
        self.commit();
        let mut change = mem::take(&mut self.change);
        if self.is_snippet {
            change.is_snippet = true;
        }
        change
    }
}
<commit_msg>Support multiple file edits in AssistBuilder<commit_after>//! See `AssistContext`

use std::mem;

use algo::find_covering_element;
use hir::Semantics;
use ide_db::base_db::{FileId, FileRange};
use ide_db::{
    label::Label,
    source_change::{SourceChange, SourceFileEdit},
    RootDatabase,
};
use syntax::{
    algo::{self, find_node_at_offset, SyntaxRewriter},
    AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange, TextSize,
    TokenAtOffset,
};
use text_edit::{TextEdit, TextEditBuilder};

use crate::{
    assist_config::{AssistConfig, SnippetCap},
    Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist,
};

/// `AssistContext` allows to apply an assist or check if it could be applied.
///
/// Assists use a somewhat over-engineered approach, given the current needs.
/// The assists workflow consists of two phases. In the first phase, a user asks
/// for the list of available assists. In the second phase, the user picks a
/// particular assist and it gets applied.
///
/// There are two peculiarities here:
///
/// * first, we ideally avoid computing more things then necessary to answer "is
///   assist applicable" in the first phase.
/// * second, when we are applying assist, we don't have a guarantee that there
///   weren't any changes between the point when user asked for assists and when
///   they applied a particular assist. So, when applying assist, we need to do
///   all the checks from scratch.
///
/// To avoid repeating the same code twice for both "check" and "apply"
/// functions, we use an approach reminiscent of that of Django's function based
/// views dealing with forms. Each assist receives a runtime parameter,
/// `resolve`. It first check if an edit is applicable (potentially computing
/// info required to compute the actual edit). If it is applicable, and
/// `resolve` is `true`, it then computes the actual edit.
///
/// So, to implement the original assists workflow, we can first apply each edit
/// with `resolve = false`, and then applying the selected edit again, with
/// `resolve = true` this time.
///
/// Note, however, that we don't actually use such two-phase logic at the
/// moment, because the LSP API is pretty awkward in this place, and it's much
/// easier to just compute the edit eagerly :-)
pub(crate) struct AssistContext<'a> {
    pub(crate) config: &'a AssistConfig,
    pub(crate) sema: Semantics<'a, RootDatabase>,
    pub(crate) frange: FileRange,
    source_file: SourceFile,
}

impl<'a> AssistContext<'a> {
    pub(crate) fn new(
        sema: Semantics<'a, RootDatabase>,
        config: &'a AssistConfig,
        frange: FileRange,
    ) -> AssistContext<'a> {
        let source_file = sema.parse(frange.file_id);
        AssistContext { config, sema, frange, source_file }
    }

    pub(crate) fn db(&self) -> &RootDatabase {
        self.sema.db
    }

    // NB, this ignores active selection.
    pub(crate) fn offset(&self) -> TextSize {
        self.frange.range.start()
    }

    pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
        self.source_file.syntax().token_at_offset(self.offset())
    }
    pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
        self.token_at_offset().find(|it| it.kind() == kind)
    }
    pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
        find_node_at_offset(self.source_file.syntax(), self.offset())
    }
    pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
        self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset())
    }
    pub(crate) fn covering_element(&self) -> SyntaxElement {
        find_covering_element(self.source_file.syntax(), self.frange.range)
    }
    // FIXME: remove
    pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
        find_covering_element(self.source_file.syntax(), range)
    }
}

pub(crate) struct Assists {
    resolve: bool,
    file: FileId,
    buf: Vec<(Assist, Option<SourceChange>)>,
    allowed: Option<Vec<AssistKind>>,
}

impl Assists {
    pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
        Assists {
            resolve: true,
            file: ctx.frange.file_id,
            buf: Vec::new(),
            allowed: ctx.config.allowed.clone(),
        }
    }

    pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
        Assists {
            resolve: false,
            file: ctx.frange.file_id,
            buf: Vec::new(),
            allowed: ctx.config.allowed.clone(),
        }
    }

    pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
        assert!(!self.resolve);
        self.finish()
            .into_iter()
            .map(|(label, edit)| {
                assert!(edit.is_none());
                label
            })
            .collect()
    }

    pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> {
        assert!(self.resolve);
        self.finish()
            .into_iter()
            .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() })
            .collect()
    }

    pub(crate) fn add(
        &mut self,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistBuilder),
    ) -> Option<()> {
        if !self.is_allowed(&id) {
            return None;
        }
        let label = Label::new(label.into());
        let assist = Assist { id, label, group: None, target };
        self.add_impl(assist, f)
    }

    pub(crate) fn add_group(
        &mut self,
        group: &GroupLabel,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistBuilder),
    ) -> Option<()> {
        if !self.is_allowed(&id) {
            return None;
        }
        let label = Label::new(label.into());
        let assist = Assist { id, label, group: Some(group.clone()), target };
        self.add_impl(assist, f)
    }

    fn add_impl(&mut self, assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
        let source_change = if self.resolve {
            let mut builder = AssistBuilder::new(self.file);
            f(&mut builder);
            Some(builder.finish())
        } else {
            None
        };

        self.buf.push((assist, source_change));
        Some(())
    }

    fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> {
        self.buf.sort_by_key(|(label, _edit)| label.target.len());
        self.buf
    }

    fn is_allowed(&self, id: &AssistId) -> bool {
        match &self.allowed {
            Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)),
            None => true,
        }
    }
}

pub(crate) struct AssistBuilder {
    edit: TextEditBuilder,
    file_id: FileId,
    is_snippet: bool,
    source_file_edits: Vec<SourceFileEdit>,
}

impl AssistBuilder {
    pub(crate) fn new(file_id: FileId) -> AssistBuilder {
        AssistBuilder {
            edit: TextEdit::builder(),
            file_id,
            is_snippet: false,
            source_file_edits: Vec::default(),
        }
    }

    pub(crate) fn edit_file(&mut self, file_id: FileId) {
        self.commit();
        self.file_id = file_id;
    }

    fn commit(&mut self) {
        let edit = mem::take(&mut self.edit).finish();
        if !edit.is_empty() {
            match self.source_file_edits.binary_search_by_key(&self.file_id, |edit| edit.file_id) {
                Ok(idx) => self.source_file_edits[idx]
                    .edit
                    .union(edit)
                    .expect("overlapping edits for same file"),
                Err(idx) => self
                    .source_file_edits
                    .insert(idx, SourceFileEdit { file_id: self.file_id, edit }),
            }
        }
    }

    /// Remove specified `range` of text.
    pub(crate) fn delete(&mut self, range: TextRange) {
        self.edit.delete(range)
    }
    /// Append specified `text` at the given `offset`
    pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
        self.edit.insert(offset, text.into())
    }
    /// Append specified `snippet` at the given `offset`
    pub(crate) fn insert_snippet(
        &mut self,
        _cap: SnippetCap,
        offset: TextSize,
        snippet: impl Into<String>,
    ) {
        self.is_snippet = true;
        self.insert(offset, snippet);
    }
    /// Replaces specified `range` of text with a given string.
    pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
        self.edit.replace(range, replace_with.into())
    }
    /// Replaces specified `range` of text with a given `snippet`.
    pub(crate) fn replace_snippet(
        &mut self,
        _cap: SnippetCap,
        range: TextRange,
        snippet: impl Into<String>,
    ) {
        self.is_snippet = true;
        self.replace(range, snippet);
    }
    pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
        algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
    }
    pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
        let node = rewriter.rewrite_root().unwrap();
        let new = rewriter.rewrite(&node);
        algo::diff(&node, &new).into_text_edit(&mut self.edit);
    }

    // FIXME: kill this API
    /// Get access to the raw `TextEditBuilder`.
    pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
        &mut self.edit
    }

    fn finish(mut self) -> SourceChange {
        self.commit();
        SourceChange {
            source_file_edits: mem::take(&mut self.source_file_edits),
            file_system_edits: Default::default(),
            is_snippet: self.is_snippet,
        }
    }
}
<|endoftext|>