<commit_before>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![experimental]

use registry::*;
use ty;
use common;
use std::io::Writer;

static TAB_WIDTH: uint = 4;

pub struct StructGenerator<'a, W: 'a> {
    ns: Ns,
    writer: &'a mut W,
    registry: &'a Registry,
    indent: uint,
}

impl<'a, W: Writer> StructGenerator<'a, W> {
    fn new<'a>(writer: &'a mut W, registry: &'a Registry, ns: Ns) -> StructGenerator<'a, W> {
        StructGenerator {
            ns: ns,
            writer: writer,
            registry: registry,
            indent: 0,
        }
    }

    fn incr_indent(&mut self) {
        self.indent += 1;
    }

    fn decr_indent(&mut self) {
        if self.indent > 0 { self.indent -= 1 }
    }

    #[allow(unused_must_use)]
    fn write_str(&mut self, s: &str) {
        self.writer.write(s.as_bytes());
    }

    fn write_indent(&mut self) {
        for _ in range(0, TAB_WIDTH * self.indent) {
            self.write_str(" ");
        }
    }

    fn write_line(&mut self, s: &str) {
        self.write_indent();
        self.write_str(s);
        self.write_str("\n");
    }

    fn write_enum(&mut self, enm: &Enum) {
        let ident = if (enm.ident.as_slice().char_at(0)).is_digit() {
            format!("_{}", enm.ident)
        } else {
            enm.ident.clone()
        };

        let ty = {
            let regex = regex!(r"\(\((\w+)\).+\)");

            if (regex).is_match(enm.value.as_slice()) {
                // if the value is ((Type)value), then the type is types::Type
                regex.replace(enm.value.as_slice(), "types::$1")

            } else if enm.value.as_slice().starts_with("\"") {
                "&'static str".to_string()

            } else {
                match ident.as_slice() {
                    "TRUE" | "FALSE" => "types::GLboolean",
                    _ => match enm.ty {
                        Some(ref s) if s.as_slice() == "ull" => "types::GLuint64",
                        _ => "types::GLenum"
                    }
                }.to_string()
            }
        };

        let value = {
            let ref value = enm.value;

            // replacing "((EGLType)0)" by "0 as types::EGLType"
            let regex = regex!(r"\(\((EGL\w+)\)0\)");
            let value = regex.replace(value.as_slice(), "0 as types::$1");

            // replacing "((Type)value)" by "value"
            let regex = regex!(r"\(\(\w+\)(.+)\)");
            let value = regex.replace(value.as_slice(), "$1");

            value
        };

        self.write_line("#[stable]");
        self.write_line("#[allow(dead_code)]");
        self.write_line(format!("pub static {}: {} = {};", ident, ty, value).as_slice())
    }

    fn write_enums(&mut self) {
        for e in self.registry.enum_iter() {
            self.write_enum(e);
        }
    }

    fn write_header(&mut self) {
        self.write_line("mod __gl_imports {");
        self.write_line("    extern crate libc;");
        self.write_line("    pub use std::mem;");
        self.write_line("}");
    }

    fn write_type_aliases(&mut self) {
        self.write_line("#[stable]");
        self.write_line("pub mod types {");
        self.incr_indent();
        self.write_line("");
        match self.ns {
            Gl | Gles1 | Gles2 => {
                for alias in ty::GL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
            }
            Glx => {
                for alias in ty::GL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::X_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::GLX_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
            }
            Wgl => {
                for alias in ty::GL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::WIN_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::WGL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
            }
            Egl => {
                for alias in ty::GL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::EGL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
            }
        }
        self.decr_indent();
        self.write_line("}");
    }

    fn write_fnptr_struct_def(&mut self) {
        self.write_line("#[allow(dead_code)]");
        self.write_line("pub struct FnPtr {");
        self.write_line("    f: *const __gl_imports::libc::c_void,");
        self.write_line("    is_loaded: bool,");
        self.write_line("}");
        self.write_line("");
        self.write_line("impl FnPtr {");
        self.write_line("    fn new(ptr: *const __gl_imports::libc::c_void, failing_fn: *const __gl_imports::libc::c_void) -> FnPtr {");
        self.write_line("        if ptr.is_null() {");
        self.write_line("            FnPtr { f: failing_fn, is_loaded: false }");
        self.write_line("        } else {");
        self.write_line("            FnPtr { f: ptr, is_loaded: true }");
        self.write_line("        }");
        self.write_line("    }");
        self.write_line("");
        self.write_line("    #[inline]");
        self.write_line("    #[allow(dead_code)]");
        self.write_line("    pub fn is_loaded(&self) -> bool {");
        self.write_line("        self.is_loaded");
        self.write_line("    }");
        self.write_line("}");
    }

    fn write_failing_fns(&mut self) {
        self.write_line("mod failing {");
        self.incr_indent();
        self.write_line("use super::types;");
        self.write_line("use super::__gl_imports;");
        self.write_line("");
        for c in self.registry.cmd_iter() {
            self.write_line("#[allow(unused_variable)] #[allow(non_snake_case)] #[allow(dead_code)]");
            self.write_line(format!(
                "pub extern \"system\" fn {name}({params}){return_suffix} {{ \
                    fail!(\"`{name}` was not loaded\") \
                }}",
                name = c.proto.ident,
                params = common::gen_param_list(c, true),
                return_suffix = common::gen_return_suffix(c)
            ).as_slice());
        }
        self.decr_indent();
        self.write_line("}");
    }

    fn write_struct(&mut self) {
        let ns = self.ns;
        self.write_line("#[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(dead_code)]");
        self.write_line("#[stable]");
        self.write_line(format!("pub struct {:c} {{", ns).as_slice());
        self.incr_indent();
        for c in self.registry.cmd_iter() {
            self.write_line(format!(
                "{name}: FnPtr,",
                name = c.proto.ident,
            ).as_slice());
        }
        self.decr_indent();
        self.write_line("}");
    }

    fn write_impl(&mut self) {
        let ns = self.ns;
        self.write_line(format!("impl {:c} {{", ns).as_slice());
        self.incr_indent();
        let ns = self.ns;
        self.write_line("/// Load each OpenGL symbol using a custom load function. This allows for the");
        self.write_line("/// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.");
        self.write_line("///");
        self.write_line("/// ~~~ignore");
        self.write_line("/// let gl = Gl::load_with(|s| glfw.get_proc_address(s));");
        self.write_line("/// ~~~");
        self.write_line("#[unstable]");

        self.write_line(format!(
            "#[allow(dead_code)]
            #[allow(unused_variable)]
            pub fn load_with(loadfn: |symbol: &str| -> *const __gl_imports::libc::c_void) -> {:c} {{", ns
        ).as_slice());
        self.incr_indent();

        self.write_line(format!("{:c} {{", ns).as_slice());
        self.incr_indent();
        for c in self.registry.cmd_iter() {
            self.write_line(format!(
                "{name}: FnPtr::new(loadfn(\"{symbol}\"), failing::{name} as *const __gl_imports::libc::c_void),",
                name = c.proto.ident,
                symbol = common::gen_symbol_name(&ns, c)
            ).as_slice());
        }
        self.decr_indent();
        self.write_line("}");

        self.decr_indent();
        self.write_line("}");
        self.write_line("");
        for c in self.registry.cmd_iter() {
            self.write_line(
                if c.is_safe {
                    format!(
                        "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                        #[inline] #[unstable] pub fn {name}(&self, {params}){return_suffix} {{ \
                            unsafe {{ \
                                __gl_imports::mem::transmute::<_, extern \"system\" fn({types}){return_suffix}>\
                                    (self.{name}.f)({idents}) \
                            }} \
                        }}",
                        name = c.proto.ident,
                        params = common::gen_param_list(c, true),
                        types = common::gen_param_ty_list(c),
                        return_suffix = common::gen_return_suffix(c),
                        idents = common::gen_param_ident_list(c),
                    )
                } else {
                    format!(
                        "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                        #[inline] #[unstable] pub unsafe fn {name}(&self, {typed_params}){return_suffix} {{ \
                            __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) {return_suffix}>\
                                (self.{name}.f)({idents}) \
                        }}",
                        name = c.proto.ident,
                        typed_params = common::gen_param_list(c, true),
                        return_suffix = common::gen_return_suffix(c),
                        idents = common::gen_param_ident_list(c),
                    )
                }.as_slice()
            );
        }

        self.decr_indent();
        self.write_line("}");
    }

    pub fn write(writer: &mut W, registry: &Registry, ns: Ns) {
        let mut gen = StructGenerator::new(writer, registry, ns);

        // header with imports
        gen.write_header();
        gen.write_line("");

        // type aliases
        gen.write_type_aliases();
        gen.write_line("");

        // enums definitions
        gen.write_enums();
        gen.write_line("");

        // FnPtr struct def
        gen.write_fnptr_struct_def();
        gen.write_line("");

        // failing functions to assign to the function pointers
        gen.write_failing_fns();
        gen.write_line("");

        // struct for storing function pointers
        gen.write_struct();
        gen.write_line("");

        // opengl functions as methods on the struct
        gen.write_impl();
        gen.write_line("");
    }
}
<commit_msg>Fix missing `pub` in struct generator<commit_after>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![experimental]

use registry::*;
use ty;
use common;
use std::io::Writer;

static TAB_WIDTH: uint = 4;

pub struct StructGenerator<'a, W: 'a> {
    ns: Ns,
    writer: &'a mut W,
    registry: &'a Registry,
    indent: uint,
}

impl<'a, W: Writer> StructGenerator<'a, W> {
    fn new<'a>(writer: &'a mut W, registry: &'a Registry, ns: Ns) -> StructGenerator<'a, W> {
        StructGenerator {
            ns: ns,
            writer: writer,
            registry: registry,
            indent: 0,
        }
    }

    fn incr_indent(&mut self) {
        self.indent += 1;
    }

    fn decr_indent(&mut self) {
        if self.indent > 0 { self.indent -= 1 }
    }

    #[allow(unused_must_use)]
    fn write_str(&mut self, s: &str) {
        self.writer.write(s.as_bytes());
    }

    fn write_indent(&mut self) {
        for _ in range(0, TAB_WIDTH * self.indent) {
            self.write_str(" ");
        }
    }

    fn write_line(&mut self, s: &str) {
        self.write_indent();
        self.write_str(s);
        self.write_str("\n");
    }

    fn write_enum(&mut self, enm: &Enum) {
        let ident = if (enm.ident.as_slice().char_at(0)).is_digit() {
            format!("_{}", enm.ident)
        } else {
            enm.ident.clone()
        };

        let ty = {
            let regex = regex!(r"\(\((\w+)\).+\)");

            if (regex).is_match(enm.value.as_slice()) {
                // if the value is ((Type)value), then the type is types::Type
                regex.replace(enm.value.as_slice(), "types::$1")

            } else if enm.value.as_slice().starts_with("\"") {
                "&'static str".to_string()

            } else {
                match ident.as_slice() {
                    "TRUE" | "FALSE" => "types::GLboolean",
                    _ => match enm.ty {
                        Some(ref s) if s.as_slice() == "ull" => "types::GLuint64",
                        _ => "types::GLenum"
                    }
                }.to_string()
            }
        };

        let value = {
            let ref value = enm.value;

            // replacing "((EGLType)0)" by "0 as types::EGLType"
            let regex = regex!(r"\(\((EGL\w+)\)0\)");
            let value = regex.replace(value.as_slice(), "0 as types::$1");

            // replacing "((Type)value)" by "value"
            let regex = regex!(r"\(\(\w+\)(.+)\)");
            let value = regex.replace(value.as_slice(), "$1");

            value
        };

        self.write_line("#[stable]");
        self.write_line("#[allow(dead_code)]");
        self.write_line(format!("pub static {}: {} = {};", ident, ty, value).as_slice())
    }

    fn write_enums(&mut self) {
        for e in self.registry.enum_iter() {
            self.write_enum(e);
        }
    }

    fn write_header(&mut self) {
        self.write_line("mod __gl_imports {");
        self.write_line("    extern crate libc;");
        self.write_line("    pub use std::mem;");
        self.write_line("}");
    }

    fn write_type_aliases(&mut self) {
        self.write_line("#[stable]");
        self.write_line("pub mod types {");
        self.incr_indent();
        self.write_line("");
        match self.ns {
            Gl | Gles1 | Gles2 => {
                for alias in ty::GL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
            }
            Glx => {
                for alias in ty::GL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::X_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::GLX_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
            }
            Wgl => {
                for alias in ty::GL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::WIN_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::WGL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
            }
            Egl => {
                for alias in ty::GL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
                for alias in ty::EGL_ALIASES.iter() {
                    self.write_line("#[allow(non_camel_case_types)]");
                    self.write_line("#[allow(non_snake_case)]");
                    self.write_line("#[allow(dead_code)]");
                    self.write_line(*alias)
                }
            }
        }
        self.decr_indent();
        self.write_line("}");
    }

    fn write_fnptr_struct_def(&mut self) {
        self.write_line("#[allow(dead_code)]");
        self.write_line("pub struct FnPtr {");
        self.write_line("    f: *const __gl_imports::libc::c_void,");
        self.write_line("    is_loaded: bool,");
        self.write_line("}");
        self.write_line("");
        self.write_line("impl FnPtr {");
        self.write_line("    fn new(ptr: *const __gl_imports::libc::c_void, failing_fn: *const __gl_imports::libc::c_void) -> FnPtr {");
        self.write_line("        if ptr.is_null() {");
        self.write_line("            FnPtr { f: failing_fn, is_loaded: false }");
        self.write_line("        } else {");
        self.write_line("            FnPtr { f: ptr, is_loaded: true }");
        self.write_line("        }");
        self.write_line("    }");
        self.write_line("");
        self.write_line("    #[inline]");
        self.write_line("    #[allow(dead_code)]");
        self.write_line("    pub fn is_loaded(&self) -> bool {");
        self.write_line("        self.is_loaded");
        self.write_line("    }");
        self.write_line("}");
    }

    fn write_failing_fns(&mut self) {
        self.write_line("mod failing {");
        self.incr_indent();
        self.write_line("use super::types;");
        self.write_line("use super::__gl_imports;");
        self.write_line("");
        for c in self.registry.cmd_iter() {
            self.write_line("#[allow(unused_variable)] #[allow(non_snake_case)] #[allow(dead_code)]");
            self.write_line(format!(
                "pub extern \"system\" fn {name}({params}){return_suffix} {{ \
                    fail!(\"`{name}` was not loaded\") \
                }}",
                name = c.proto.ident,
                params = common::gen_param_list(c, true),
                return_suffix = common::gen_return_suffix(c)
            ).as_slice());
        }
        self.decr_indent();
        self.write_line("}");
    }

    fn write_struct(&mut self) {
        let ns = self.ns;
        self.write_line("#[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(dead_code)]");
        self.write_line("#[stable]");
        self.write_line(format!("pub struct {:c} {{", ns).as_slice());
        self.incr_indent();
        for c in self.registry.cmd_iter() {
            self.write_line(format!(
                "pub {name}: FnPtr,",
                name = c.proto.ident,
            ).as_slice());
        }
        self.decr_indent();
        self.write_line("}");
    }

    fn write_impl(&mut self) {
        let ns = self.ns;
        self.write_line(format!("impl {:c} {{", ns).as_slice());
        self.incr_indent();
        let ns = self.ns;
        self.write_line("/// Load each OpenGL symbol using a custom load function. This allows for the");
        self.write_line("/// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.");
        self.write_line("///");
        self.write_line("/// ~~~ignore");
        self.write_line("/// let gl = Gl::load_with(|s| glfw.get_proc_address(s));");
        self.write_line("/// ~~~");
        self.write_line("#[unstable]");

        self.write_line(format!(
            "#[allow(dead_code)]
            #[allow(unused_variable)]
            pub fn load_with(loadfn: |symbol: &str| -> *const __gl_imports::libc::c_void) -> {:c} {{", ns
        ).as_slice());
        self.incr_indent();

        self.write_line(format!("{:c} {{", ns).as_slice());
        self.incr_indent();
        for c in self.registry.cmd_iter() {
            self.write_line(format!(
                "{name}: FnPtr::new(loadfn(\"{symbol}\"), failing::{name} as *const __gl_imports::libc::c_void),",
                name = c.proto.ident,
                symbol = common::gen_symbol_name(&ns, c)
            ).as_slice());
        }
        self.decr_indent();
        self.write_line("}");

        self.decr_indent();
        self.write_line("}");
        self.write_line("");
        for c in self.registry.cmd_iter() {
            self.write_line(
                if c.is_safe {
                    format!(
                        "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                        #[inline] #[unstable] pub fn {name}(&self, {params}){return_suffix} {{ \
                            unsafe {{ \
                                __gl_imports::mem::transmute::<_, extern \"system\" fn({types}){return_suffix}>\
                                    (self.{name}.f)({idents}) \
                            }} \
                        }}",
                        name = c.proto.ident,
                        params = common::gen_param_list(c, true),
                        types = common::gen_param_ty_list(c),
                        return_suffix = common::gen_return_suffix(c),
                        idents = common::gen_param_ident_list(c),
                    )
                } else {
                    format!(
                        "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                        #[inline] #[unstable] pub unsafe fn {name}(&self, {typed_params}){return_suffix} {{ \
                            __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) {return_suffix}>\
                                (self.{name}.f)({idents}) \
                        }}",
                        name = c.proto.ident,
                        typed_params = common::gen_param_list(c, true),
                        return_suffix = common::gen_return_suffix(c),
                        idents = common::gen_param_ident_list(c),
                    )
                }.as_slice()
            );
        }

        self.decr_indent();
        self.write_line("}");
    }

    pub fn write(writer: &mut W, registry: &Registry, ns: Ns) {
        let mut gen = StructGenerator::new(writer, registry, ns);

        // header with imports
        gen.write_header();
        gen.write_line("");

        // type aliases
        gen.write_type_aliases();
        gen.write_line("");

        // enums definitions
        gen.write_enums();
        gen.write_line("");

        // FnPtr struct def
        gen.write_fnptr_struct_def();
        gen.write_line("");

        // failing functions to assign to the function pointers
        gen.write_failing_fns();
        gen.write_line("");

        // struct for storing function pointers
        gen.write_struct();
        gen.write_line("");

        // opengl functions as methods on the struct
        gen.write_impl();
        gen.write_line("");
    }
}
<|endoftext|>


<commit_before>use std::{env, ffi::OsString, fs, path::Path};

use binary::{
    async_trait::async_trait,
    binaries_dir, binary_clone_box,
    eyre::{bail, Result},
    tracing, Binary, BinaryTrait,
};

/// A `BinaryTrait` for `asdf`
///
/// This sets the `$ASDF_DATA_DIR` to be the same as the Stencila `binaries` directory.
/// Given that `asdf` uses the same structure for this directory as Stencila, it means that
/// executables that are installed by `asdf` have the same expected location as those installed
/// by Stencila.
pub struct AsdfBinary;

#[async_trait]
impl BinaryTrait for AsdfBinary {
    fn spec(&self) -> Binary {
        Binary::new("asdf", &[], &[])
    }

    binary_clone_box!();

    async fn versions(&self, _os: &str) -> Result<Vec<String>> {
        self.versions_github_releases("asdf-vm", "asdf")
            .await
            .map(|versions| self.semver_versions_sorted(versions))
    }

    fn run_env(&self, version: Option<String>) -> Vec<(OsString, OsString)> {
        if let Ok(dir) = self.dir(version, false) {
            let binaries_dir = binaries_dir();
            if let Ok(..) = dir.strip_prefix(&binaries_dir) {
                let asdf_config_file = dir.join(".asdfrc").into_os_string();
                let asdf_dir = dir.into_os_string();
                let asdf_data_dir = binaries_dir.into_os_string();
                let path = OsString::from(env::var("PATH").unwrap_or_default());
                let path = env::join_paths(&[asdf_dir.clone(), path.clone()]).unwrap_or(path);
                return vec![
                    ("ASDF_CONFIG_FILE".into(), asdf_config_file),
                    ("ASDF_DIR".into(), asdf_dir),
                    ("ASDF_DATA_DIR".into(), asdf_data_dir),
                    ("PATH".into(), path),
                ];
            }
        }
        Vec::new()
    }

    async fn install_version(
        &self,
        version: &str,
        dest: &Path,
        os: &str,
        _arch: &str,
    ) -> Result<()> {
        if os == "windows" {
            bail!("`asdf` can not be install on Windows")
        }

        let url = format!(
            "https://github.com/asdf-vm/asdf/archive/refs/tags/v{version}.tar.gz",
            version = version
        );
        let filename = format!("asdf-v{version}.tar.gz", version = version);
        let archive = self.download(&url, Some(filename), None).await?;

        self.extract(&archive, 1, dest)?;
        self.executables(dest, &["bin/asdf"])?;

        // TODO: use a setting to determine the keep downloads policy for both Stencila and asdf
        fs::write(dest.join(".asdfrc"), "always_keep_download = yes\n")?;

        Ok(())
    }
}

impl AsdfBinary {
    /// List all versions for an `asdf` package
    pub async fn list_all(package: &str) -> Result<Vec<String>> {
        let asdf = AsdfBinary {}.ensure().await?;

        asdf.run_with(&["plugin", "add", package], None, None)
            .await
            .ok();

        let output = asdf
            .command()
            .args(&["list", "all", package])
            .output()
            .await?;
        let versions: Vec<String> = std::str::from_utf8(&output.stdout)?
            .lines()
            .map(String::from)
            .collect();

        Ok(versions)
    }

    /// Install a version for an `asdf` package
    ///
    /// Calls `uninstall` first because even with an empty directory it
    /// will consider it is already installed and do nothing.
    pub async fn install(package: &str, version: &str) -> Result<()> {
        let asdf = AsdfBinary {}.ensure().await?;

        asdf.run_with(&["plugin", "add", package], None, None)
            .await
            .ok();
        asdf.run_with(&["uninstall", package, version], None, None)
            .await
            .ok();
        asdf.run_with(
            &["install", package, version],
            Some(tracing::Level::INFO),
            Some(tracing::Level::INFO),
        )
        .await?;

        Ok(())
    }
}
<commit_msg>refactor(ASDF binary): Add utility methods<commit_after>use std::{
    env,
    ffi::{OsStr, OsString},
    fs,
    path::Path,
};

use binary::{
    async_trait::async_trait,
    binaries_dir, binary_clone_box,
    eyre::{bail, Result},
    Binary, BinaryTrait,
};

/// A `BinaryTrait` for `asdf`
///
/// This sets the `$ASDF_DATA_DIR` to be the same as the Stencila `binaries` directory.
/// Given that `asdf` uses the same structure for this directory as Stencila, it means that
/// executables that are installed by `asdf` have the same expected location as those installed
/// by Stencila.
pub struct AsdfBinary;

#[async_trait]
impl BinaryTrait for AsdfBinary {
    fn spec(&self) -> Binary {
        Binary::new("asdf", &[], &[])
    }

    binary_clone_box!();

    async fn versions(&self, _os: &str) -> Result<Vec<String>> {
        self.versions_github_releases("asdf-vm", "asdf")
            .await
            .map(|versions| self.semver_versions_sorted(versions))
    }

    fn run_env(&self, version: Option<String>) -> Vec<(OsString, OsString)> {
        if let Ok(dir) = self.dir(version, false) {
            let binaries_dir = binaries_dir();
            if let Ok(..) = dir.strip_prefix(&binaries_dir) {
                let asdf_config_file = dir.join(".asdfrc").into_os_string();
                let asdf_dir = dir.into_os_string();
                let asdf_data_dir = binaries_dir.into_os_string();
                let path = OsString::from(env::var("PATH").unwrap_or_default());
                let path = env::join_paths(&[asdf_dir.clone(), path.clone()]).unwrap_or(path);
                return vec![
                    ("ASDF_CONFIG_FILE".into(), asdf_config_file),
                    ("ASDF_DIR".into(), asdf_dir),
                    ("ASDF_DATA_DIR".into(), asdf_data_dir),
                    ("PATH".into(), path),
                ];
            }
        }
        Vec::new()
    }

    async fn install_version(
        &self,
        version: &str,
        dest: &Path,
        os: &str,
        _arch: &str,
    ) -> Result<()> {
        if os == "windows" {
            bail!("`asdf` can not be install on Windows")
        }

        let url = format!(
            "https://github.com/asdf-vm/asdf/archive/refs/tags/v{version}.tar.gz",
            version = version
        );
        let filename = format!("asdf-v{version}.tar.gz", version = version);
        let archive = self.download(&url, Some(filename), None).await?;

        self.extract(&archive, 1, dest)?;
        self.executables(dest, &["bin/asdf"])?;

        // TODO: use a setting to determine the keep downloads policy for both Stencila and asdf
        fs::write(dest.join(".asdfrc"), "always_keep_download = yes\n")?;

        Ok(())
    }
}

impl AsdfBinary {
    /// Add an `asdf` plugin
    pub async fn add_plugin(plugin: &str, repo: Option<&str>) -> Result<()> {
        let asdf = AsdfBinary {}.ensure().await?;

        let mut args = vec!["plugin", "add", plugin];
        if let Some(repo) = repo {
            args.push(repo);
        }
        asdf.run(&args).await?;

        Ok(())
    }

    /// List all versions for an `asdf` package
    pub async fn list_all(package: &str) -> Result<Vec<String>> {
        let asdf = AsdfBinary {}.ensure().await?;

        asdf.run_with(&["plugin", "add", package], None, None)
            .await
            .ok();

        let output = asdf
            .command()
            .args(&["list", "all", package])
            .output()
            .await?;
        let versions: Vec<String> = std::str::from_utf8(&output.stdout)?
            .lines()
            .map(String::from)
            .collect();

        Ok(versions)
    }

    /// Install a version for an `asdf` package
    ///
    /// Calls `uninstall` first because even with an empty directory it
    /// will consider it is already installed and do nothing.
    pub async fn install(package: &str, version: &str) -> Result<()> {
        let asdf = AsdfBinary {}.ensure().await?;

        asdf.run_with(&["plugin", "add", package], None, None)
            .await
            .ok();
        asdf.run_with(&["uninstall", package, version], None, None)
            .await
            .ok();
        asdf.run(&["install", package, version]).await?;

        Ok(())
    }

    /// Install a version for an `asdf` package with env vars
    pub async fn install_with(
        package: &str,
        version: &str,
        vars: &[(impl AsRef<OsStr>, impl AsRef<OsStr>)],
    ) -> Result<()> {
        let mut asdf = AsdfBinary {}.ensure().await?;

        asdf.env_list(vars);
        asdf.run(&["install", package, version]).await?;

        Ok(())
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

mod builder;
mod endian_scalar;
mod follow;
mod primitives;
mod push;
mod table;
mod vector;
mod vtable;
mod vtable_writer;

pub use builder::FlatBufferBuilder;
pub use endian_scalar::{EndianScalar, emplace_scalar, read_scalar, read_scalar_at, byte_swap_f32, byte_swap_f64};
pub use follow::{Follow, FollowStart};
pub use primitives::*;
pub use push::Push;
pub use table::{Table, buffer_has_identifier, get_root, get_size_prefixed_root};
pub use vector::{SafeSliceAccess, Vector, follow_cast_ref};
pub use vtable::field_index_to_field_offset;

// TODO(rw): Unify `create_vector` and `create_vector_direct` by using
//           `Into<Vector<...>>`.
// TODO(rw): Split fill ops in builder into fill_small, fill_big like in C++.
<commit_msg>Rust: Add basic crate-level documentation<commit_after>/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! # FlatBuffers
//!
//! A library for memory-efficient serialization of data.
//!
//! This crate provides runtime support for the FlatBuffers format in the Rust programming language.
//! To use this crate, first generate code with the `flatc` compiler, as described here: https://google.github.io/flatbuffers/
//! Then, include that code into your project.
//! Finally, add this crate to your `Cargo.toml`.
//!
//! At this time, Rust support is experimental, and APIs may change between minor versions.
//!
//! At this time, to generate Rust code, you will need the latest `master` version of `flatc`, available from here: https://github.com/google/flatbuffers
//! (On OSX, you can install FlatBuffers from `HEAD` with the Homebrew package manager.)

mod builder;
mod endian_scalar;
mod follow;
mod primitives;
mod push;
mod table;
mod vector;
mod vtable;
mod vtable_writer;

pub use builder::FlatBufferBuilder;
pub use endian_scalar::{EndianScalar, emplace_scalar, read_scalar, read_scalar_at, byte_swap_f32, byte_swap_f64};
pub use follow::{Follow, FollowStart};
pub use primitives::*;
pub use push::Push;
pub use table::{Table, buffer_has_identifier, get_root, get_size_prefixed_root};
pub use vector::{SafeSliceAccess, Vector, follow_cast_ref};
pub use vtable::field_index_to_field_offset;

// TODO(rw): Unify `create_vector` and `create_vector_direct` by using
//           `Into<Vector<...>>`.
// TODO(rw): Split fill ops in builder into fill_small, fill_big like in C++.
<|endoftext|>


<commit_before>#![feature(const_fn)]
#![feature(drop_types_in_const)]
#![feature(proc_macro)]

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

extern crate bootstrap_rs as bootstrap;
extern crate fiber;

use fiber::FiberId;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
use std::mem;
use std::sync::Mutex;
use std::time::Duration;

#[cfg(target_os="windows")]
#[path="windows.rs"]
pub mod platform;

thread_local! {
    static CONTEXT: RefCell<Context> = RefCell::new(Context::new());
}

lazy_static! {
    static ref CONTEXT_MAP: Mutex<HashMap<FiberId, Context>> = Mutex::new(HashMap::with_capacity(1024));
    static ref EVENTS: Mutex<Vec<Event>> = Mutex::new(Vec::new());
}

/// Swaps the currently tracked execution context with the specified context.
pub fn switch_context(old: FiberId, new: FiberId) {
    with_context(|stack| {
        let timestamp = platform::timestamp();

        // If there are stopwatches on the stack then we need to end the flow event.
        if stack.len() > 0 {
            push_event(Event {
                name: stack[0].name,
                cat: String::new(),
                ph: "f",
                id: fiber::current().map(FiberId::primitive_id),
                ts: timestamp,
                tid: platform::thread_id(),
                pid: 0,
                bp: "e",
            });
        }

        // Push an end event for each of the time slices.
        for stopwatch in stack.iter().rev() {
            push_event(Event {
                name: stopwatch.name,
                cat: String::new(),
                ph: "E",
                id: fiber::current().map(FiberId::primitive_id),
                ts: timestamp,
                tid: platform::thread_id(),
                pid: 0,
                bp: "e",
            });
        }
    });

    let mut context_map = CONTEXT_MAP.lock().expect("Unable to acquire lock on context map");

    let new_context = context_map.remove(&new).unwrap_or(Context::new());
    let old_context = with_context(move |context| {
        let mut new_context = new_context;
        mem::swap(context, &mut new_context);
        new_context
    });

    context_map.insert(old, old_context);

    with_context(|stack| {
        let timestamp = platform::timestamp();

        // Push an end event for each of the time slices.
        for stopwatch in stack.iter() {
            push_event(Event {
                name: stopwatch.name,
                cat: String::new(),
                ph: "B",
                id: fiber::current().map(FiberId::primitive_id),
                ts: timestamp,
                tid: platform::thread_id(),
                pid: 0,
                bp: "e",
            });
        }

        // If there are stopwatches on the stack then we need to end the flow event.
        if stack.len() > 0 {
            push_event(Event {
                name: stack[0].name,
                cat: String::new(),
                ph: "s",
                id: fiber::current().map(FiberId::primitive_id),
                ts: timestamp,
                tid: platform::thread_id(),
                pid: 0,
                bp: "e",
            });
        }
    });
}

/// Writes the events history to a string.
pub fn write_events_to_string() -> String {
    let events = EVENTS.lock().expect("Events mutex got poisoned");
    serde_json::to_string(&*events).unwrap()
}

pub struct Stopwatch {
    name: &'static str,
}

impl Stopwatch {
    pub fn new(name: &'static str) -> Stopwatch {
        push_event(Event {
            name: name,
            cat: String::new(),
            ph: "B",
            id: fiber::current().map(FiberId::primitive_id),
            ts: platform::timestamp(),
            tid: platform::thread_id(),
            pid: 0, // TODO: Do we care about tracking process ID?
            bp: "e",
        });

        with_context(|stack| {
            // The first event on the stack also needs a flow event.
            if stack.len() == 0 {
                push_event(Event {
                    name: name,
                    cat: String::new(),
                    ph: "s",
                    id: fiber::current().map(FiberId::primitive_id),
                    ts: platform::timestamp(),
                    tid: platform::thread_id(),
                    pid: 0,
                    bp: "e",
                })
            }

            stack.push(StopwatchData { name: name });
        });

        Stopwatch {
            name: name,
        }
    }

    pub fn with_budget(name: &'static str, _budget: Duration) -> Stopwatch {
        // TODO: We should actually do something with the budget, right?
        Stopwatch::new(name)
    }
}

impl Drop for Stopwatch {
    fn drop(&mut self) {
        with_context(|stack| {
            let stopwatch = stack.pop().expect("No stopwatch popped, stack is corrupted");
            assert_eq!(self.name, stopwatch.name, "Stack got corrupted I guess");

            if stack.len() == 0 {
                push_event(Event {
                    name: self.name,
                    cat: String::new(),
                    ph: "f",
                    id: fiber::current().map(FiberId::primitive_id),
                    ts: platform::timestamp(),
                    tid: platform::thread_id(),
                    pid: 0,
                    bp: "e",
                });
            }
        });

        push_event(Event {
            name: self.name,
            cat: String::new(),
            ph: "E",
            id: fiber::current().map(FiberId::primitive_id),
            ts: platform::timestamp(),
            tid: platform::thread_id(),
            pid: 0, // TODO: Do we care about tracking process ID?
            bp: "e",
        });
    }
}

#[derive(Debug, Serialize)]
struct Event {
    /// Human-readable name for the event.
    name: &'static str,

    /// Event category.
    cat: String,

    /// Event phase (i.e. the event type).
    ph: &'static str,

    /// Timestamp in microseconds.
    ts: i64,

    /// Process ID for the event.
    pid: usize,

    /// Thread ID for the event.
    tid: usize,

    id: Option<isize>,

    bp: &'static str,
}

fn push_event(event: Event) {
    let mut events = EVENTS.lock().expect("Events mutex got poisoned");
    events.push(event);
}

#[derive(Debug, Clone, Copy)]
struct StopwatchData {
    name: &'static str,
}

type Context = Vec<StopwatchData>;

fn with_context<F, T>(func: F) -> T
    where F: FnOnce(&mut Context) -> T
{
    CONTEXT.with(move |context_cell| {
        let mut context = context_cell.borrow_mut();
        func(&mut *context)
    })
}

pub struct PrettyDuration(pub Duration);

impl Debug for PrettyDuration {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
        let secs = self.0.as_secs();
        let millis = (self.0.subsec_nanos() / 1_000_000) % 1_000;
        let micros = (self.0.subsec_nanos() / 1_000) % 1_000;
        if secs > 0 {
            write!(formatter, "{}s {}ms {}μs", secs, millis, micros)
        } else {
            write!(formatter, "{}ms {}μs", millis, micros)
        }
    }
}
<commit_msg>Remove flow event tracking<commit_after>#![feature(const_fn)]
#![feature(drop_types_in_const)]
#![feature(proc_macro)]

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

extern crate bootstrap_rs as bootstrap;
extern crate fiber;

use fiber::FiberId;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
use std::mem;
use std::sync::Mutex;
use std::time::Duration;

#[cfg(target_os="windows")]
#[path="windows.rs"]
pub mod platform;

thread_local! {
    static CONTEXT: RefCell<Context> = RefCell::new(Context::new());
}

lazy_static! {
    static ref CONTEXT_MAP: Mutex<HashMap<FiberId, Context>> = Mutex::new(HashMap::with_capacity(1024));
    static ref EVENTS: Mutex<Vec<Event>> = Mutex::new(Vec::new());
}

/// Swaps the currently tracked execution context with the specified context.
pub fn switch_context(old: FiberId, new: FiberId) {
    with_context(|stack| {
        let timestamp = platform::timestamp();

        // Push an end event for each of the time slices.
        for stopwatch in stack.iter().rev() {
            push_event(Event {
                name: stopwatch.name,
                cat: String::new(),
                ph: "E",
                ts: timestamp,
                tid: platform::thread_id(),
                pid: 0,
            });
        }
    });

    let mut context_map = CONTEXT_MAP.lock().expect("Unable to acquire lock on context map");

    let new_context = context_map.remove(&new).unwrap_or(Context::new());
    let old_context = with_context(move |context| {
        let mut new_context = new_context;
        mem::swap(context, &mut new_context);
        new_context
    });

    context_map.insert(old, old_context);

    with_context(|stack| {
        let timestamp = platform::timestamp();

        // Push an end event for each of the time slices.
        for stopwatch in stack.iter() {
            push_event(Event {
                name: stopwatch.name,
                cat: String::new(),
                ph: "B",
                ts: timestamp,
                tid: platform::thread_id(),
                pid: 0,
            });
        }
    });
}

/// Writes the events history to a string.
pub fn write_events_to_string() -> String {
    let events = EVENTS.lock().expect("Events mutex got poisoned");
    serde_json::to_string(&*events).unwrap()
}

pub struct Stopwatch {
    name: &'static str,
}

impl Stopwatch {
    pub fn new(name: &'static str) -> Stopwatch {
        push_event(Event {
            name: name,
            cat: String::new(),
            ph: "B",
            ts: platform::timestamp(),
            tid: platform::thread_id(),
            pid: 0, // TODO: Do we care about tracking process ID?
        });

        with_context(|stack| {
            stack.push(StopwatchData { name: name });
        });

        Stopwatch {
            name: name,
        }
    }

    pub fn with_budget(name: &'static str, _budget: Duration) -> Stopwatch {
        // TODO: We should actually do something with the budget, right?
        Stopwatch::new(name)
    }
}

impl Drop for Stopwatch {
    fn drop(&mut self) {
        with_context(|stack| {
            let stopwatch = stack.pop().expect("No stopwatch popped, stack is corrupted");
            assert_eq!(self.name, stopwatch.name, "Stack got corrupted I guess");
        });

        push_event(Event {
            name: self.name,
            cat: String::new(),
            ph: "E",
            ts: platform::timestamp(),
            tid: platform::thread_id(),
            pid: 0, // TODO: Do we care about tracking process ID?
        });
    }
}

#[derive(Debug, Serialize)]
struct Event {
    /// Human-readable name for the event.
    name: &'static str,

    /// Event category.
    cat: String,

    /// Event phase (i.e. the event type).
    ph: &'static str,

    /// Timestamp in microseconds.
    ts: i64,

    /// Process ID for the event.
    pid: usize,

    /// Thread ID for the event.
    tid: usize,
}

fn push_event(event: Event) {
    let mut events = EVENTS.lock().expect("Events mutex got poisoned");
    events.push(event);
}

#[derive(Debug, Clone, Copy)]
struct StopwatchData {
    name: &'static str,
}

type Context = Vec<StopwatchData>;

fn with_context<F, T>(func: F) -> T
    where F: FnOnce(&mut Context) -> T
{
    CONTEXT.with(move |context_cell| {
        let mut context = context_cell.borrow_mut();
        func(&mut *context)
    })
}

pub struct PrettyDuration(pub Duration);

impl Debug for PrettyDuration {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
        let secs = self.0.as_secs();
        let millis = (self.0.subsec_nanos() / 1_000_000) % 1_000;
        let micros = (self.0.subsec_nanos() / 1_000) % 1_000;
        if secs > 0 {
            write!(formatter, "{}s {}ms {}μs", secs, millis, micros)
        } else {
            write!(formatter, "{}ms {}μs", millis, micros)
        }
    }
}
<|endoftext|>


<commit_before>//! Completion of names from the current scope in type position.

use hir::{HirDisplay, ScopeDef};
use syntax::{ast, AstNode, SyntaxKind};

use crate::{
    context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
    render::render_type_inference,
    CompletionContext, Completions,
};

pub(crate) fn complete_type_path(
    acc: &mut Completions,
    ctx: &CompletionContext,
    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
    location: &TypeLocation,
) {
    let _p = profile::span("complete_type_path");

    let scope_def_applicable = |def| {
        use hir::{GenericParam::*, ModuleDef::*};
        match def {
            ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
            // no values in type places
            ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
            // unless its a constant in a generic arg list position
            ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
                matches!(location, TypeLocation::GenericArgList(_))
            }
            ScopeDef::ImplSelfType(_) => {
                !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
            }
            // Don't suggest attribute macros and derives.
            ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
            // Type things are fine
            ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_))
            | ScopeDef::AdtSelfType(_)
            | ScopeDef::Unknown
            | ScopeDef::GenericParam(TypeParam(_)) => true,
        }
    };

    let add_assoc_item = |acc: &mut Completions, item| match item {
        hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => {
            acc.add_const(ctx, ct)
        }
        hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
    };

    match qualified {
        Qualified::Infer => ctx
            .traits_in_scope()
            .iter()
            .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
            .for_each(|item| add_assoc_item(acc, item)),
        Qualified::With { resolution: None, .. } => {}
        Qualified::With { resolution: Some(resolution), .. } => {
            // Add associated types on type parameters and `Self`.
            ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
                acc.add_type_alias(ctx, alias);
                None::<()>
            });

            match resolution {
                hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
                    let module_scope = module.scope(ctx.db, Some(ctx.module));
                    for (name, def) in module_scope {
                        if scope_def_applicable(def) {
                            acc.add_path_resolution(ctx, path_ctx, name, def);
                        }
                    }
                }
                hir::PathResolution::Def(
                    def @ (hir::ModuleDef::Adt(_)
                    | hir::ModuleDef::TypeAlias(_)
                    | hir::ModuleDef::BuiltinType(_)),
                ) => {
                    let ty = match def {
                        hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
                        hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
                        hir::ModuleDef::BuiltinType(builtin) => builtin.ty(ctx.db),
                        _ => return,
                    };

                    // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
                    // (where AssocType is defined on a trait, not an inherent impl)

                    ctx.iterate_path_candidates(&ty, |item| {
                        add_assoc_item(acc, item);
                    });

                    // Iterate assoc types separately
                    ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
                        if let hir::AssocItem::TypeAlias(ty) = item {
                            acc.add_type_alias(ctx, ty)
                        }
                        None::<()>
                    });
                }
                hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
                    // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
                    for item in t.items(ctx.db) {
                        add_assoc_item(acc, item);
                    }
                }
                hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
                    let ty = match resolution {
                        hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
                        hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
                        _ => return,
                    };

                    ctx.iterate_path_candidates(&ty, |item| {
                        add_assoc_item(acc, item);
                    });
                }
                _ => (),
            }
        }
        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
        Qualified::No => {
            match location {
                TypeLocation::TypeBound => {
                    acc.add_nameref_keywords_with_colon(ctx);
                    ctx.process_all_names(&mut |name, res| {
                        let add_resolution = match res {
                            ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
                                mac.is_fn_like(ctx.db)
                            }
                            ScopeDef::ModuleDef(
                                hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_),
                            ) => true,
                            _ => false,
                        };
                        if add_resolution {
                            acc.add_path_resolution(ctx, path_ctx, name, res);
                        }
                    });
                    return;
                }
                TypeLocation::GenericArgList(Some(arg_list)) => {
                    let in_assoc_type_arg = ctx
                        .original_token
                        .parent_ancestors()
                        .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);

                    if !in_assoc_type_arg {
                        if let Some(path_seg) =
                            arg_list.syntax().parent().and_then(ast::PathSegment::cast)
                        {
                            if path_seg
                                .syntax()
                                .ancestors()
                                .find_map(ast::TypeBound::cast)
                                .is_some()
                            {
                                if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
                                    trait_,
                                ))) = ctx.sema.resolve_path(&path_seg.parent_path())
                                {
                                    let arg_idx = arg_list
                                        .generic_args()
                                        .filter(|arg| {
                                            arg.syntax().text_range().end()
                                                < ctx.original_token.text_range().start()
                                        })
                                        .count();

                                    let n_required_params =
                                        trait_.type_or_const_param_count(ctx.sema.db, true);
                                    if arg_idx >= n_required_params {
                                        trait_
                                            .items_with_supertraits(ctx.sema.db)
                                            .into_iter()
                                            .for_each(|it| {
                                                if let hir::AssocItem::TypeAlias(alias) = it {
                                                    cov_mark::hit!(
                                                        complete_assoc_type_in_generics_list
                                                    );
                                                    acc.add_type_alias_with_eq(ctx, alias);
                                                }
                                            });
                                    }

                                    let n_params =
                                        trait_.type_or_const_param_count(ctx.sema.db, false);
                                    if arg_idx >= n_params {
                                        return; // only show assoc types
                                    }
                                }
                            }
                        }
                    }
                }
                _ => {}
            };

            acc.add_nameref_keywords_with_colon(ctx);
            ctx.process_all_names(&mut |name, def| {
                if scope_def_applicable(def) {
                    acc.add_path_resolution(ctx, path_ctx, name, def);
                }
            });
        }
    }
}

pub(crate) fn complete_ascribed_type(
    acc: &mut Completions,
    ctx: &CompletionContext,
    path_ctx: &PathCompletionCtx,
    ascription: &TypeAscriptionTarget,
) -> Option<()> {
    if !path_ctx.is_trivial_path() {
        return None;
    }
    let x = match ascription {
        TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
            ctx.sema.type_of_pat(pat.as_ref()?)
        }
        TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType(exp) => {
            ctx.sema.type_of_expr(exp.as_ref()?)
        }
    }?
    .adjusted();
    let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
    acc.add(render_type_inference(ty_string, ctx));
    None
}
<commit_msg>check arg_idx >= n_params only if arg_idx >= n_required_params<commit_after>//! Completion of names from the current scope in type position.

use hir::{HirDisplay, ScopeDef};
use syntax::{ast, AstNode, SyntaxKind};

use crate::{
    context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
    render::render_type_inference,
    CompletionContext, Completions,
};

pub(crate) fn complete_type_path(
    acc: &mut Completions,
    ctx: &CompletionContext,
    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
    location: &TypeLocation,
) {
    let _p = profile::span("complete_type_path");

    let scope_def_applicable = |def| {
        use hir::{GenericParam::*, ModuleDef::*};
        match def {
            ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
            // no values in type places
            ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
            // unless its a constant in a generic arg list position
            ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
                matches!(location, TypeLocation::GenericArgList(_))
            }
            ScopeDef::ImplSelfType(_) => {
                !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
            }
            // Don't suggest attribute macros and derives.
            ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
            // Type things are fine
            ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_))
            | ScopeDef::AdtSelfType(_)
            | ScopeDef::Unknown
            | ScopeDef::GenericParam(TypeParam(_)) => true,
        }
    };

    let add_assoc_item = |acc: &mut Completions, item| match item {
        hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => {
            acc.add_const(ctx, ct)
        }
        hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
    };

    match qualified {
        Qualified::Infer => ctx
            .traits_in_scope()
            .iter()
            .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
            .for_each(|item| add_assoc_item(acc, item)),
        Qualified::With { resolution: None, .. } => {}
        Qualified::With { resolution: Some(resolution), .. } => {
            // Add associated types on type parameters and `Self`.
            ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
                acc.add_type_alias(ctx, alias);
                None::<()>
            });

            match resolution {
                hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
                    let module_scope = module.scope(ctx.db, Some(ctx.module));
                    for (name, def) in module_scope {
                        if scope_def_applicable(def) {
                            acc.add_path_resolution(ctx, path_ctx, name, def);
                        }
                    }
                }
                hir::PathResolution::Def(
                    def @ (hir::ModuleDef::Adt(_)
                    | hir::ModuleDef::TypeAlias(_)
                    | hir::ModuleDef::BuiltinType(_)),
                ) => {
                    let ty = match def {
                        hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
                        hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
                        hir::ModuleDef::BuiltinType(builtin) => builtin.ty(ctx.db),
                        _ => return,
                    };

                    // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
                    // (where AssocType is defined on a trait, not an inherent impl)

                    ctx.iterate_path_candidates(&ty, |item| {
                        add_assoc_item(acc, item);
                    });

                    // Iterate assoc types separately
                    ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
                        if let hir::AssocItem::TypeAlias(ty) = item {
                            acc.add_type_alias(ctx, ty)
                        }
                        None::<()>
                    });
                }
                hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
                    // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
                    for item in t.items(ctx.db) {
                        add_assoc_item(acc, item);
                    }
                }
                hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
                    let ty = match resolution {
                        hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
                        hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
                        _ => return,
                    };

                    ctx.iterate_path_candidates(&ty, |item| {
                        add_assoc_item(acc, item);
                    });
                }
                _ => (),
            }
        }
        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
        Qualified::No => {
            match location {
                TypeLocation::TypeBound => {
                    acc.add_nameref_keywords_with_colon(ctx);
                    ctx.process_all_names(&mut |name, res| {
                        let add_resolution = match res {
                            ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
                                mac.is_fn_like(ctx.db)
                            }
                            ScopeDef::ModuleDef(
                                hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_),
                            ) => true,
                            _ => false,
                        };
                        if add_resolution {
                            acc.add_path_resolution(ctx, path_ctx, name, res);
                        }
                    });
                    return;
                }
                TypeLocation::GenericArgList(Some(arg_list)) => {
                    let in_assoc_type_arg = ctx
                        .original_token
                        .parent_ancestors()
                        .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);

                    if !in_assoc_type_arg {
                        if let Some(path_seg) =
                            arg_list.syntax().parent().and_then(ast::PathSegment::cast)
                        {
                            if path_seg
                                .syntax()
                                .ancestors()
                                .find_map(ast::TypeBound::cast)
                                .is_some()
                            {
                                if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
                                    trait_,
                                ))) = ctx.sema.resolve_path(&path_seg.parent_path())
                                {
                                    let arg_idx = arg_list
                                        .generic_args()
                                        .filter(|arg| {
                                            arg.syntax().text_range().end()
                                                < ctx.original_token.text_range().start()
                                        })
                                        .count();

                                    let n_required_params =
                                        trait_.type_or_const_param_count(ctx.sema.db, true);
                                    if arg_idx >= n_required_params {
                                        trait_
                                            .items_with_supertraits(ctx.sema.db)
                                            .into_iter()
                                            .for_each(|it| {
                                                if let hir::AssocItem::TypeAlias(alias) = it {
                                                    cov_mark::hit!(
                                                        complete_assoc_type_in_generics_list
                                                    );
                                                    acc.add_type_alias_with_eq(ctx, alias);
                                                }
                                            });

                                        let n_params =
                                            trait_.type_or_const_param_count(ctx.sema.db, false);
                                        if arg_idx >= n_params {
                                            return; // only show assoc types
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                _ => {}
            };

            acc.add_nameref_keywords_with_colon(ctx);
            ctx.process_all_names(&mut |name, def| {
                if scope_def_applicable(def) {
                    acc.add_path_resolution(ctx, path_ctx, name, def);
                }
            });
        }
    }
}

pub(crate) fn complete_ascribed_type(
    acc: &mut Completions,
    ctx: &CompletionContext,
    path_ctx: &PathCompletionCtx,
    ascription: &TypeAscriptionTarget,
) -> Option<()> {
    if !path_ctx.is_trivial_path() {
        return None;
    }
    let x = match ascription {
        TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
            ctx.sema.type_of_pat(pat.as_ref()?)
        }
        TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType(exp) => {
            ctx.sema.type_of_expr(exp.as_ref()?)
        }
    }?
    .adjusted();
    let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
    acc.add(render_type_inference(ty_string, ctx));
    None
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here
use assists::utils::TryEnum;
use syntax::{
    ast::{self, AstNode},
    TextRange, TextSize,
};
use text_edit::TextEdit;

use crate::{
    completion::{
        completion_config::SnippetCap,
        completion_context::CompletionContext,
        completion_item::{Builder, CompletionKind, Completions},
    },
    CompletionItem, CompletionItemKind,
};

pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.config.enable_postfix_completions {
        return;
    }

    let dot_receiver = match &ctx.dot_receiver {
        Some(it) => it,
        None => return,
    };

    let receiver_text =
        get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);

    let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
        Some(it) => it,
        None => return,
    };

    let cap = match ctx.config.snippet_cap {
        Some(it) => it,
        None => return,
    };
    let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
    if let Some(try_enum) = &try_enum {
        match try_enum {
            TryEnum::Result => {
                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "ifl",
                    "if let Ok {}",
                    &format!("if let Ok($1) = {} {{\n    $0\n}}", receiver_text),
                )
                .add_to(acc);

                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "while",
                    "while let Ok {}",
                    &format!("while let Ok($1) = {} {{\n    $0\n}}", receiver_text),
                )
                .add_to(acc);
            }
            TryEnum::Option => {
                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "ifl",
                    "if let Some {}",
                    &format!("if let Some($1) = {} {{\n    $0\n}}", receiver_text),
                )
                .add_to(acc);

                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "while",
                    "while let Some {}",
                    &format!("while let Some($1) = {} {{\n    $0\n}}", receiver_text),
                )
                .add_to(acc);
            }
        }
    } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
        postfix_snippet(
            ctx,
            cap,
            &dot_receiver,
            "if",
            "if expr {}",
            &format!("if {} {{\n    $0\n}}", receiver_text),
        )
        .add_to(acc);
        postfix_snippet(
            ctx,
            cap,
            &dot_receiver,
            "while",
            "while expr {}",
            &format!("while {} {{\n    $0\n}}", receiver_text),
        )
        .add_to(acc);
        postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
            .add_to(acc);
    }

    postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
        .add_to(acc);
    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "refm",
        "&mut expr",
        &format!("&mut {}", receiver_text),
    )
    .add_to(acc);

    // The rest of the postfix completions create an expression that moves an argument,
    // so it's better to consider references now to avoid breaking the compilation
    let dot_receiver = include_references(dot_receiver);
    let receiver_text =
        get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);

    match try_enum {
        Some(try_enum) => match try_enum {
            TryEnum::Result => {
                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "match",
                    "match expr {}",
                    &format!("match {} {{\n    Ok(${{1:_}}) => {{$2}},\n    Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
                )
                .add_to(acc);
            }
            TryEnum::Option => {
                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "match",
                    "match expr {}",
                    &format!(
                        "match {} {{\n    Some(${{1:_}}) => {{$2}},\n    None => {{$0}},\n}}",
                        receiver_text
                    ),
                )
                .add_to(acc);
            }
        },
        None => {
            postfix_snippet(
                ctx,
                cap,
                &dot_receiver,
                "match",
                "match expr {}",
                &format!("match {} {{\n    ${{1:_}} => {{$0}},\n}}", receiver_text),
            )
            .add_to(acc);
        }
    }

    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "box",
        "Box::new(expr)",
        &format!("Box::new({})", receiver_text),
    )
    .add_to(acc);

    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "dbg",
        "dbg!(expr)",
        &format!("dbg!({})", receiver_text),
    )
    .add_to(acc);

    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "call",
        "function(expr)",
        &format!("${{1}}({})", receiver_text),
    )
    .add_to(acc);
}

fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
    if receiver_is_ambiguous_float_literal {
        let text = receiver.syntax().text();
        let without_dot = ..text.len() - TextSize::of('.');
        text.slice(without_dot).to_string()
    } else {
        receiver.to_string()
    }
}

fn include_references(initial_element: &ast::Expr) -> ast::Expr {
    let mut resulting_element = initial_element.clone();
    while let Some(parent_ref_element) =
        resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
    {
        resulting_element = ast::Expr::from(parent_ref_element);
    }
    resulting_element
}

fn postfix_snippet(
    ctx: &CompletionContext,
    cap: SnippetCap,
    receiver: &ast::Expr,
    label: &str,
    detail: &str,
    snippet: &str,
) -> Builder {
    let edit = {
        let receiver_syntax = receiver.syntax();
        let receiver_range = ctx.sema.original_range(receiver_syntax).range;
        let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
        TextEdit::replace(delete_range, snippet.to_string())
    };
    CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
        .detail(detail)
        .kind(CompletionItemKind::Snippet)
        .snippet_edit(cap, edit)
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::completion::{
        test_utils::{check_edit, completion_list},
        CompletionKind,
    };

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture, CompletionKind::Postfix);
        expect.assert_eq(&actual)
    }

    #[test]
    fn postfix_completion_works_for_trivial_path_expression() {
        check(
            r#"
fn main() {
    let bar = true;
    bar.<|>
}
"#,
            expect![[r#"
                sn box   Box::new(expr)
                sn call  function(expr)
                sn dbg   dbg!(expr)
                sn if    if expr {}
                sn match match expr {}
                sn not   !expr
                sn ref   &expr
                sn refm  &mut expr
                sn while while expr {}
            "#]],
        );
    }

    #[test]
    fn postfix_type_filtering() {
        check(
            r#"
fn main() {
    let bar: u8 = 12;
    bar.<|>
}
"#,
            expect![[r#"
                sn box   Box::new(expr)
                sn call  function(expr)
                sn dbg   dbg!(expr)
                sn match match expr {}
                sn ref   &expr
                sn refm  &mut expr
            "#]],
        )
    }

    #[test]
    fn option_iflet() {
        check_edit(
            "ifl",
            r#"
enum Option<T> { Some(T), None }

fn main() {
    let bar = Option::Some(true);
    bar.<|>
}
"#,
            r#"
enum Option<T> { Some(T), None }

fn main() {
    let bar = Option::Some(true);
    if let Some($1) = bar {
    $0
}
}
"#,
        );
    }

    #[test]
    fn result_match() {
        check_edit(
            "match",
            r#"
enum Result<T, E> { Ok(T), Err(E) }

fn main() {
    let bar = Result::Ok(true);
    bar.<|>
}
"#,
            r#"
enum Result<T, E> { Ok(T), Err(E) }

fn main() {
    let bar = Result::Ok(true);
    match bar {
    Ok(${1:_}) => {$2},
    Err(${3:_}) => {$0},
}
}
"#,
        );
    }

    #[test]
    fn postfix_completion_works_for_ambiguous_float_literal() {
        check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
    }

    #[test]
    fn works_in_simple_macro() {
        check_edit(
            "dbg",
            r#"
macro_rules! m { ($e:expr) => { $e } }
fn main() {
    let bar: u8 = 12;
    m!(bar.d<|>)
}
"#,
            r#"
macro_rules! m { ($e:expr) => { $e } }
fn main() {
    let bar: u8 = 12;
    m!(dbg!(bar))
}
"#,
        );
    }

    #[test]
    fn postfix_completion_for_references() {
        check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
        check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
    }
}
<commit_msg>Add ok postfix completion<commit_after>//! FIXME: write short doc here
use assists::utils::TryEnum;
use syntax::{
    ast::{self, AstNode},
    TextRange, TextSize,
};
use text_edit::TextEdit;

use crate::{
    completion::{
        completion_config::SnippetCap,
        completion_context::CompletionContext,
        completion_item::{Builder, CompletionKind, Completions},
    },
    CompletionItem, CompletionItemKind,
};

pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.config.enable_postfix_completions {
        return;
    }

    let dot_receiver = match &ctx.dot_receiver {
        Some(it) => it,
        None => return,
    };

    let receiver_text =
        get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);

    let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
        Some(it) => it,
        None => return,
    };

    let cap = match ctx.config.snippet_cap {
        Some(it) => it,
        None => return,
    };
    let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
    if let Some(try_enum) = &try_enum {
        match try_enum {
            TryEnum::Result => {
                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "ifl",
                    "if let Ok {}",
                    &format!("if let Ok($1) = {} {{\n    $0\n}}", receiver_text),
                )
                .add_to(acc);

                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "while",
                    "while let Ok {}",
                    &format!("while let Ok($1) = {} {{\n    $0\n}}", receiver_text),
                )
                .add_to(acc);
            }
            TryEnum::Option => {
                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "ifl",
                    "if let Some {}",
                    &format!("if let Some($1) = {} {{\n    $0\n}}", receiver_text),
                )
                .add_to(acc);

                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "while",
                    "while let Some {}",
                    &format!("while let Some($1) = {} {{\n    $0\n}}", receiver_text),
                )
                .add_to(acc);
            }
        }
    } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
        postfix_snippet(
            ctx,
            cap,
            &dot_receiver,
            "if",
            "if expr {}",
            &format!("if {} {{\n    $0\n}}", receiver_text),
        )
        .add_to(acc);
        postfix_snippet(
            ctx,
            cap,
            &dot_receiver,
            "while",
            "while expr {}",
            &format!("while {} {{\n    $0\n}}", receiver_text),
        )
        .add_to(acc);
        postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
            .add_to(acc);
    }

    postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
        .add_to(acc);
    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "refm",
        "&mut expr",
        &format!("&mut {}", receiver_text),
    )
    .add_to(acc);

    // The rest of the postfix completions create an expression that moves an argument,
    // so it's better to consider references now to avoid breaking the compilation
    let dot_receiver = include_references(dot_receiver);
    let receiver_text =
        get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);

    match try_enum {
        Some(try_enum) => match try_enum {
            TryEnum::Result => {
                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "match",
                    "match expr {}",
                    &format!("match {} {{\n    Ok(${{1:_}}) => {{$2}},\n    Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
                )
                .add_to(acc);
            }
            TryEnum::Option => {
                postfix_snippet(
                    ctx,
                    cap,
                    &dot_receiver,
                    "match",
                    "match expr {}",
                    &format!(
                        "match {} {{\n    Some(${{1:_}}) => {{$2}},\n    None => {{$0}},\n}}",
                        receiver_text
                    ),
                )
                .add_to(acc);
            }
        },
        None => {
            postfix_snippet(
                ctx,
                cap,
                &dot_receiver,
                "match",
                "match expr {}",
                &format!("match {} {{\n    ${{1:_}} => {{$0}},\n}}", receiver_text),
            )
            .add_to(acc);
        }
    }

    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "box",
        "Box::new(expr)",
        &format!("Box::new({})", receiver_text),
    )
    .add_to(acc);

    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "ok",
        "Ok(expr)",
        &format!("Ok({})", receiver_text),
    )
    .add_to(acc);

    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "dbg",
        "dbg!(expr)",
        &format!("dbg!({})", receiver_text),
    )
    .add_to(acc);

    postfix_snippet(
        ctx,
        cap,
        &dot_receiver,
        "call",
        "function(expr)",
        &format!("${{1}}({})", receiver_text),
    )
    .add_to(acc);
}

fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
    if receiver_is_ambiguous_float_literal {
        let text = receiver.syntax().text();
        let without_dot = ..text.len() - TextSize::of('.');
        text.slice(without_dot).to_string()
    } else {
        receiver.to_string()
    }
}

fn include_references(initial_element: &ast::Expr) -> ast::Expr {
    let mut resulting_element = initial_element.clone();
    while let Some(parent_ref_element) =
        resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
    {
        resulting_element = ast::Expr::from(parent_ref_element);
    }
    resulting_element
}

fn postfix_snippet(
    ctx: &CompletionContext,
    cap: SnippetCap,
    receiver: &ast::Expr,
    label: &str,
    detail: &str,
    snippet: &str,
) -> Builder {
    let edit = {
        let receiver_syntax = receiver.syntax();
        let receiver_range = ctx.sema.original_range(receiver_syntax).range;
        let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
        TextEdit::replace(delete_range, snippet.to_string())
    };
    CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
        .detail(detail)
        .kind(CompletionItemKind::Snippet)
        .snippet_edit(cap, edit)
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::completion::{
        test_utils::{check_edit, completion_list},
        CompletionKind,
    };

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture, CompletionKind::Postfix);
        expect.assert_eq(&actual)
    }

    #[test]
    fn postfix_completion_works_for_trivial_path_expression() {
        check(
            r#"
fn main() {
    let bar = true;
    bar.<|>
}
"#,
            expect![[r#"
                sn box   Box::new(expr)
                sn call  function(expr)
                sn dbg   dbg!(expr)
                sn if    if expr {}
                sn match match expr {}
                sn not   !expr
                sn ok    Ok(expr)
                sn ref   &expr
                sn refm  &mut expr
                sn while while expr {}
            "#]],
        );
    }

    #[test]
    fn postfix_type_filtering() {
        check(
            r#"
fn main() {
    let bar: u8 = 12;
    bar.<|>
}
"#,
            expect![[r#"
                sn box   Box::new(expr)
                sn call  function(expr)
                sn dbg   dbg!(expr)
                sn match match expr {}
                sn ok    Ok(expr)
                sn ref   &expr
                sn refm  &mut expr
            "#]],
        )
    }

    #[test]
    fn option_iflet() {
        check_edit(
            "ifl",
            r#"
enum Option<T> { Some(T), None }

fn main() {
    let bar = Option::Some(true);
    bar.<|>
}
"#,
            r#"
enum Option<T> { Some(T), None }

fn main() {
    let bar = Option::Some(true);
    if let Some($1) = bar {
    $0
}
}
"#,
        );
    }

    #[test]
    fn result_match() {
        check_edit(
            "match",
            r#"
enum Result<T, E> { Ok(T), Err(E) }

fn main() {
    let bar = Result::Ok(true);
    bar.<|>
}
"#,
            r#"
enum Result<T, E> { Ok(T), Err(E) }

fn main() {
    let bar = Result::Ok(true);
    match bar {
    Ok(${1:_}) => {$2},
    Err(${3:_}) => {$0},
}
}
"#,
        );
    }

    #[test]
    fn postfix_completion_works_for_ambiguous_float_literal() {
        check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
    }

    #[test]
    fn works_in_simple_macro() {
        check_edit(
            "dbg",
            r#"
macro_rules! m { ($e:expr) => { $e } }
fn main() {
    let bar: u8 = 12;
    m!(bar.d<|>)
}
"#,
            r#"
macro_rules! m { ($e:expr) => { $e } }
fn main() {
    let bar: u8 = 12;
    m!(dbg!(bar))
}
"#,
        );
    }

    #[test]
    fn postfix_completion_for_references() {
        check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
        check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro;

mod ast;
mod attr;
mod expand;
mod fmt;
mod prop;
mod valid;

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(Error, attributes(backtrace, error, from, source))]
pub fn derive_error(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand::derive(&input)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}
<commit_msg>Ignore a new clippy lint<commit_after>#![allow(clippy::block_in_if_condition_stmt)]

extern crate proc_macro;

mod ast;
mod attr;
mod expand;
mod fmt;
mod prop;
mod valid;

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(Error, attributes(backtrace, error, from, source))]
pub fn derive_error(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand::derive(&input)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}
<|endoftext|>