<commit_before>// Copyright 2020 Google LLC
//
// 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
//
//    https://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 function_wrapper_cpp;
pub(crate) mod type_to_cpp;

use crate::{types::QualifiedName, CppFilePair};
use indoc::indoc;
use itertools::Itertools;
use std::collections::HashSet;
use syn::Type;
use type_to_cpp::type_to_cpp;

use super::{
    analysis::fun::{
        function_wrapper::{FunctionWrapper, FunctionWrapperPayload},
        FnAnalysis,
    },
    api::Api,
    ConvertError,
};

/// Instructions for new C++ which we need to generate.
#[derive(Clone)]
pub(crate) enum AdditionalNeed {
    MakeStringConstructor,
    FunctionWrapper(Box<FunctionWrapper>),
    CTypeTypedef(QualifiedName),
    ConcreteTemplatedTypeTypedef(QualifiedName, Box<Type>),
}

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
struct Header {
    name: &'static str,
    system: bool,
}

impl Header {
    fn system(name: &'static str) -> Self {
        Header { name, system: true }
    }

    fn user(name: &'static str) -> Self {
        Header {
            name,
            system: false,
        }
    }

    fn include_stmt(&self) -> String {
        if self.system {
            format!("#include <{}>", self.name)
        } else {
            format!("#include \"{}\"", self.name)
        }
    }
}

struct AdditionalFunction {
    type_definition: Option<String>, // are output before main declarations
    declaration: Option<String>,
    headers: Vec<Header>,
}

/// Generates additional C++ glue functions needed by autocxx.
/// In some ways it would be preferable to be able to pass snippets
/// of C++ through to `cxx` for inclusion in the C++ file which it
/// generates, and perhaps we'll explore that in future. But for now,
/// autocxx generates its own _additional_ C++ files which therefore
/// need to be built and included in linking procedures.
pub(crate) struct CppCodeGenerator {
    additional_functions: Vec<AdditionalFunction>,
    inclusions: String,
}

impl CppCodeGenerator {
    pub(crate) fn generate_cpp_code(
        inclusions: String,
        apis: &[Api<FnAnalysis>],
    ) -> Result<Option<CppFilePair>, ConvertError> {
        let mut gen = CppCodeGenerator::new(inclusions);
        gen.add_needs(apis.iter().filter_map(|api| api.additional_cpp()))?;
        Ok(gen.generate())
    }

    fn new(inclusions: String) -> Self {
        CppCodeGenerator {
            additional_functions: Vec::new(),
            inclusions,
        }
    }

    fn add_needs(
        &mut self,
        additions: impl Iterator<Item = AdditionalNeed>,
    ) -> Result<(), ConvertError> {
        for need in additions {
            match need {
                AdditionalNeed::MakeStringConstructor => self.generate_string_constructor(),
                AdditionalNeed::FunctionWrapper(by_value_wrapper) => {
                    self.generate_by_value_wrapper(&by_value_wrapper)?
                }
                AdditionalNeed::CTypeTypedef(tn) => self.generate_ctype_typedef(&tn),
                AdditionalNeed::ConcreteTemplatedTypeTypedef(tn, def) => {
                    self.generate_typedef(&tn, type_to_cpp(&def)?)
                }
            }
        }
        Ok(())
    }

    fn generate(&self) -> Option<CppFilePair> {
        if self.additional_functions.is_empty() {
            None
        } else {
            let headers: HashSet<Header> = self
                .additional_functions
                .iter()
                .map(|x| x.headers.iter().cloned())
                .flatten()
                .collect();
            let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
            let type_definitions = self.concat_additional_items(|x| x.type_definition.as_ref());
            let declarations = self.concat_additional_items(|x| x.declaration.as_ref());
            let declarations = format!(
                "#ifndef __AUTOCXXGEN_H__\n#define __AUTOCXXGEN_H__\n\n{}\n{}\n{}\n{}#endif // __AUTOCXXGEN_H__\n",
                headers, self.inclusions, type_definitions, declarations
            );
            log::info!("Additional C++ decls:\n{}", declarations);
            Some(CppFilePair {
                header: declarations.into_bytes(),
                implementation: None,
                header_name: "autocxxgen.h".into(),
            })
        }
    }

    fn concat_additional_items<F>(&self, field_access: F) -> String
    where
        F: FnMut(&AdditionalFunction) -> Option<&String>,
    {
        let mut s = self
            .additional_functions
            .iter()
            .map(field_access)
            .flatten()
            .join("\n");
        s.push('\n');
        s
    }

    fn generate_string_constructor(&mut self) {
        let declaration = indoc! {"
        inline std::unique_ptr<std::string> make_string(::rust::Str str)
        { return std::make_unique<std::string>(std::string(str)); }
        "};
        let declaration = Some(declaration.into());
        self.additional_functions.push(AdditionalFunction {
            type_definition: None,
            declaration,
            headers: vec![
                Header::system("memory"),
                Header::system("string"),
                Header::user("cxx.h"),
            ],
        })
    }

    fn generate_by_value_wrapper(&mut self, details: &FunctionWrapper) -> Result<(), ConvertError> {
        // Even if the original function call is in a namespace,
        // we generate this wrapper in the global namespace.
        // We could easily do this the other way round, and when
        // cxx::bridge comes to support nested namespace mods then
        // we wil wish to do that to avoid name conflicts. However,
        // at the moment this is simpler because it avoids us having
        // to generate namespace blocks in the generated C++.
        let is_a_method = details.is_a_method;
        let name = &details.wrapper_function_name;
        let get_arg_name = |counter: usize| -> String {
            if is_a_method && counter == 0 {
                // For method calls that we generate, the first
                // argument name needs to be such that we recognize
                // it as a method in the second invocation of
                // bridge_converter after it's flowed again through
                // bindgen.
                // TODO this may not be the case any longer. We
                // may be able to remove this.
                "autocxx_gen_this".to_string()
            } else {
                format!("arg{}", counter)
            }
        };
        let args: Result<Vec<_>, _> = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, ty)| {
                Ok(format!(
                    "{} {}",
                    ty.unconverted_type()?,
                    get_arg_name(counter)
                ))
            })
            .collect();
        let args = args?.join(", ");
        let ret_type = details
            .return_conversion
            .as_ref()
            .map_or(Ok("void".to_string()), |x| x.converted_type())?;
        let declaration = format!("{} {}({})", ret_type, name, args);
        let arg_list: Result<Vec<_>, _> = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, conv)| conv.cpp_conversion(&get_arg_name(counter)))
            .collect();
        let mut arg_list = arg_list?.into_iter();
        let receiver = if is_a_method { arg_list.next() } else { None };
        let arg_list = arg_list.join(", ");
        let mut underlying_function_call = match &details.payload {
            FunctionWrapperPayload::Constructor => arg_list,
            FunctionWrapperPayload::FunctionCall(ns, id) => match receiver {
                Some(receiver) => format!("{}.{}({})", receiver, id.to_string(), arg_list),
                None => {
                    let underlying_function_call = ns
                        .into_iter()
                        .cloned()
                        .chain(std::iter::once(id.to_string()))
                        .join("::");
                    format!("{}({})", underlying_function_call, arg_list)
                }
            },
            FunctionWrapperPayload::StaticMethodCall(ns, ty_id, fn_id) => {
                let underlying_function_call = ns
                    .into_iter()
                    .cloned()
                    .chain([ty_id.to_string(), fn_id.to_string()].iter().cloned())
                    .join("::");
                format!("{}({})", underlying_function_call, arg_list)
            }
        };
        if let Some(ret) = &details.return_conversion {
            underlying_function_call =
                format!("return {}", ret.cpp_conversion(&underlying_function_call)?);
        };
        let declaration = Some(format!(
            "{} {{ {}; }}",
            declaration, underlying_function_call,
        ));
        self.additional_functions.push(AdditionalFunction {
            type_definition: None,
            declaration,
            headers: vec![Header::system("memory")],
        });
        Ok(())
    }

    fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
        let cpp_name = tn.to_cpp_name();
        self.generate_typedef(tn, cpp_name)
    }

    fn generate_typedef(&mut self, tn: &QualifiedName, definition: String) {
        let our_name = tn.get_final_item();
        self.additional_functions.push(AdditionalFunction {
            type_definition: Some(format!("typedef {} {};", definition, our_name)),
            declaration: None,
            headers: Vec::new(),
        })
    }
}
<commit_msg>Inlining fix.<commit_after>// Copyright 2020 Google LLC
//
// 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
//
//    https://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 function_wrapper_cpp;
pub(crate) mod type_to_cpp;

use crate::{types::QualifiedName, CppFilePair};
use indoc::indoc;
use itertools::Itertools;
use std::collections::HashSet;
use syn::Type;
use type_to_cpp::type_to_cpp;

use super::{
    analysis::fun::{
        function_wrapper::{FunctionWrapper, FunctionWrapperPayload},
        FnAnalysis,
    },
    api::Api,
    ConvertError,
};

/// Instructions for new C++ which we need to generate.
#[derive(Clone)]
pub(crate) enum AdditionalNeed {
    MakeStringConstructor,
    FunctionWrapper(Box<FunctionWrapper>),
    CTypeTypedef(QualifiedName),
    ConcreteTemplatedTypeTypedef(QualifiedName, Box<Type>),
}

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
struct Header {
    name: &'static str,
    system: bool,
}

impl Header {
    fn system(name: &'static str) -> Self {
        Header { name, system: true }
    }

    fn user(name: &'static str) -> Self {
        Header {
            name,
            system: false,
        }
    }

    fn include_stmt(&self) -> String {
        if self.system {
            format!("#include <{}>", self.name)
        } else {
            format!("#include \"{}\"", self.name)
        }
    }
}

struct AdditionalFunction {
    type_definition: Option<String>, // are output before main declarations
    declaration: Option<String>,
    headers: Vec<Header>,
}

/// Generates additional C++ glue functions needed by autocxx.
/// In some ways it would be preferable to be able to pass snippets
/// of C++ through to `cxx` for inclusion in the C++ file which it
/// generates, and perhaps we'll explore that in future. But for now,
/// autocxx generates its own _additional_ C++ files which therefore
/// need to be built and included in linking procedures.
pub(crate) struct CppCodeGenerator {
    additional_functions: Vec<AdditionalFunction>,
    inclusions: String,
}

impl CppCodeGenerator {
    pub(crate) fn generate_cpp_code(
        inclusions: String,
        apis: &[Api<FnAnalysis>],
    ) -> Result<Option<CppFilePair>, ConvertError> {
        let mut gen = CppCodeGenerator::new(inclusions);
        gen.add_needs(apis.iter().filter_map(|api| api.additional_cpp()))?;
        Ok(gen.generate())
    }

    fn new(inclusions: String) -> Self {
        CppCodeGenerator {
            additional_functions: Vec::new(),
            inclusions,
        }
    }

    fn add_needs(
        &mut self,
        additions: impl Iterator<Item = AdditionalNeed>,
    ) -> Result<(), ConvertError> {
        for need in additions {
            match need {
                AdditionalNeed::MakeStringConstructor => self.generate_string_constructor(),
                AdditionalNeed::FunctionWrapper(by_value_wrapper) => {
                    self.generate_by_value_wrapper(&by_value_wrapper)?
                }
                AdditionalNeed::CTypeTypedef(tn) => self.generate_ctype_typedef(&tn),
                AdditionalNeed::ConcreteTemplatedTypeTypedef(tn, def) => {
                    self.generate_typedef(&tn, type_to_cpp(&def)?)
                }
            }
        }
        Ok(())
    }

    fn generate(&self) -> Option<CppFilePair> {
        if self.additional_functions.is_empty() {
            None
        } else {
            let headers: HashSet<Header> = self
                .additional_functions
                .iter()
                .map(|x| x.headers.iter().cloned())
                .flatten()
                .collect();
            let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
            let type_definitions = self.concat_additional_items(|x| x.type_definition.as_ref());
            let declarations = self.concat_additional_items(|x| x.declaration.as_ref());
            let declarations = format!(
                "#ifndef __AUTOCXXGEN_H__\n#define __AUTOCXXGEN_H__\n\n{}\n{}\n{}\n{}#endif // __AUTOCXXGEN_H__\n",
                headers, self.inclusions, type_definitions, declarations
            );
            log::info!("Additional C++ decls:\n{}", declarations);
            Some(CppFilePair {
                header: declarations.into_bytes(),
                implementation: None,
                header_name: "autocxxgen.h".into(),
            })
        }
    }

    fn concat_additional_items<F>(&self, field_access: F) -> String
    where
        F: FnMut(&AdditionalFunction) -> Option<&String>,
    {
        let mut s = self
            .additional_functions
            .iter()
            .map(field_access)
            .flatten()
            .join("\n");
        s.push('\n');
        s
    }

    fn generate_string_constructor(&mut self) {
        let declaration = indoc! {"
        inline std::unique_ptr<std::string> make_string(::rust::Str str)
        { return std::make_unique<std::string>(std::string(str)); }
        "};
        let declaration = Some(declaration.into());
        self.additional_functions.push(AdditionalFunction {
            type_definition: None,
            declaration,
            headers: vec![
                Header::system("memory"),
                Header::system("string"),
                Header::user("cxx.h"),
            ],
        })
    }

    fn generate_by_value_wrapper(&mut self, details: &FunctionWrapper) -> Result<(), ConvertError> {
        // Even if the original function call is in a namespace,
        // we generate this wrapper in the global namespace.
        // We could easily do this the other way round, and when
        // cxx::bridge comes to support nested namespace mods then
        // we wil wish to do that to avoid name conflicts. However,
        // at the moment this is simpler because it avoids us having
        // to generate namespace blocks in the generated C++.
        let is_a_method = details.is_a_method;
        let name = &details.wrapper_function_name;
        let get_arg_name = |counter: usize| -> String {
            if is_a_method && counter == 0 {
                // For method calls that we generate, the first
                // argument name needs to be such that we recognize
                // it as a method in the second invocation of
                // bridge_converter after it's flowed again through
                // bindgen.
                // TODO this may not be the case any longer. We
                // may be able to remove this.
                "autocxx_gen_this".to_string()
            } else {
                format!("arg{}", counter)
            }
        };
        let args: Result<Vec<_>, _> = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, ty)| {
                Ok(format!(
                    "{} {}",
                    ty.unconverted_type()?,
                    get_arg_name(counter)
                ))
            })
            .collect();
        let args = args?.join(", ");
        let ret_type = details
            .return_conversion
            .as_ref()
            .map_or(Ok("void".to_string()), |x| x.converted_type())?;
        let declaration = format!("{} {}({})", ret_type, name, args);
        let arg_list: Result<Vec<_>, _> = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, conv)| conv.cpp_conversion(&get_arg_name(counter)))
            .collect();
        let mut arg_list = arg_list?.into_iter();
        let receiver = if is_a_method { arg_list.next() } else { None };
        let arg_list = arg_list.join(", ");
        let mut underlying_function_call = match &details.payload {
            FunctionWrapperPayload::Constructor => arg_list,
            FunctionWrapperPayload::FunctionCall(ns, id) => match receiver {
                Some(receiver) => format!("{}.{}({})", receiver, id.to_string(), arg_list),
                None => {
                    let underlying_function_call = ns
                        .into_iter()
                        .cloned()
                        .chain(std::iter::once(id.to_string()))
                        .join("::");
                    format!("{}({})", underlying_function_call, arg_list)
                }
            },
            FunctionWrapperPayload::StaticMethodCall(ns, ty_id, fn_id) => {
                let underlying_function_call = ns
                    .into_iter()
                    .cloned()
                    .chain([ty_id.to_string(), fn_id.to_string()].iter().cloned())
                    .join("::");
                format!("{}({})", underlying_function_call, arg_list)
            }
        };
        if let Some(ret) = &details.return_conversion {
            underlying_function_call =
                format!("return {}", ret.cpp_conversion(&underlying_function_call)?);
        };
        let declaration = Some(format!(
            "inline {} {{ {}; }}",
            declaration, underlying_function_call,
        ));
        self.additional_functions.push(AdditionalFunction {
            type_definition: None,
            declaration,
            headers: vec![Header::system("memory")],
        });
        Ok(())
    }

    fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
        let cpp_name = tn.to_cpp_name();
        self.generate_typedef(tn, cpp_name)
    }

    fn generate_typedef(&mut self, tn: &QualifiedName, definition: String) {
        let our_name = tn.get_final_item();
        self.additional_functions.push(AdditionalFunction {
            type_definition: Some(format!("typedef {} {};", definition, our_name)),
            declaration: None,
            headers: Vec::new(),
        })
    }
}
<|endoftext|>


<commit_before>use std::f64::INFINITY;
use raytracer::Intersection;
use scene::Scene;
use vec3::Vec3;

#[cfg(test)]
use geometry::prim::Prim;
#[cfg(test)]
use geometry::prims::Sphere;
#[cfg(test)]
use light::light::Light;
#[cfg(test)]
use material::materials::FlatMaterial;

pub struct Ray {
    pub origin: Vec3,
    pub direction: Vec3,
    pub inverse_dir: Vec3, // This is used to optimise ray-bbox intersection checks
    pub signs: Vec<bool> // Handle degenerate case in bbox intersection
}

impl Ray {
    pub fn new(origin: Vec3, direction: Vec3) -> Ray {
        let inv_x = 1.0 / direction.x;
        let inv_y = 1.0 / direction.y;
        let inv_z = 1.0 / direction.z;

        Ray {
            origin: origin,
            direction: direction,
            inverse_dir: Vec3 {
                x: inv_x,
                y: inv_y,
                z: inv_z
            },
            signs: vec![
                inv_x > 0.0,
                inv_y > 0.0,
                inv_z > 0.0
            ]
        }
    }

    pub fn get_nearest_hit<'a>(&'a self, scene: &'a Scene) -> Option<Intersection<'a>> {
        let t_min = 0.000001;
        let mut nearest_hit = None;
        let mut nearest_t = INFINITY;

        for prim in scene.octree.get_intersected_objects(self) {
            let intersection = prim.intersects(self, t_min, nearest_t);

            nearest_hit = match intersection {
                Some(intersection) => {
                    if intersection.t > t_min && intersection.t < nearest_t {
                        nearest_t = intersection.t;
                        Some(intersection)
                    } else {
                        nearest_hit
                    }
                },
                None => nearest_hit
            };
        }

        nearest_hit
    }

    pub fn perturb(&self, magnitude: f64) -> Ray {
        let rand_vec = Vec3::random() * magnitude;

        // Force random vectors to be in same direction as original vector
        let corrected_rand_vec = if rand_vec.dot(&self.direction) < 0.0 {
            rand_vec * -1.0
        } else {
            rand_vec
        };

        let direction = (corrected_rand_vec + self.direction).unit();

        Ray::new(self.origin, direction)
    }
}

#[test]
fn it_gets_the_nearest_hit() {
    let lights: Vec<Box<Light+Send+Sync>> = Vec::new();

    let mut prims: Vec<Box<Prim+Send+Sync>> = Vec::new();
    let mat = FlatMaterial { color: Vec3::one() };
    let sphere_top = Sphere {
        center: Vec3::zero(),
        radius: 1.0,
        material: Box::new(mat.clone()),
    };
    let sphere_mid = Sphere {
        center: Vec3 { x: -1.0, y: 0.0, z: 0.0 },
        radius: 1.0,
        material: Box::new(mat.clone()),
    };
    let sphere_bot = Sphere {
        center: Vec3 { x: -2.0, y: 0.0, z: 0.0 },
        radius: 1.0,
        material: Box::new(mat.clone()),
    };
    prims.push(Box::new(sphere_top));
    prims.push(Box::new(sphere_mid));
    prims.push(Box::new(sphere_bot));

    println!("Generating octree...");
    let octree = prims.into_iter().collect();
    println!("Octree generated...");

    let scene = Scene {
        lights: lights,
        background: Vec3::one(),
        octree: octree,
        skybox: None
    };

    let intersecting_ray = Ray::new(
        Vec3 { x: 10.0, y: 0.0, z: 0.0 },
        Vec3 { x: -1.0, y: 0.0, z: 0.0 }
    );

    let intersection = intersecting_ray.get_nearest_hit(&scene);
    assert_eq!(1.0, intersection.unwrap().position.x);

    let non_intersecting_ray = Ray::new(
        Vec3 { x: 10.0, y: 0.0, z: 0.0 },
        Vec3 { x: 1.0, y: 0.0, z: 0.0 });

    let non_intersection = non_intersecting_ray.get_nearest_hit(&scene);
    assert!(non_intersection.is_none());
}
<commit_msg>ray: Remove allocation for signs<commit_after>use std::f64::INFINITY;
use raytracer::Intersection;
use scene::Scene;
use vec3::Vec3;

#[cfg(test)]
use geometry::prim::Prim;
#[cfg(test)]
use geometry::prims::Sphere;
#[cfg(test)]
use light::light::Light;
#[cfg(test)]
use material::materials::FlatMaterial;

pub struct Ray {
    pub origin: Vec3,
    pub direction: Vec3,
    pub inverse_dir: Vec3, // This is used to optimise ray-bbox intersection checks
    pub signs: [bool; 3], // Handle degenerate case in bbox intersection
}

impl Ray {
    pub fn new(origin: Vec3, direction: Vec3) -> Ray {
        let inv_x = 1.0 / direction.x;
        let inv_y = 1.0 / direction.y;
        let inv_z = 1.0 / direction.z;

        Ray {
            origin: origin,
            direction: direction,
            inverse_dir: Vec3 {
                x: inv_x,
                y: inv_y,
                z: inv_z
            },
            signs: [
                inv_x > 0.0,
                inv_y > 0.0,
                inv_z > 0.0
            ]
        }
    }

    pub fn get_nearest_hit<'a>(&'a self, scene: &'a Scene) -> Option<Intersection<'a>> {
        let t_min = 0.000001;
        let mut nearest_hit = None;
        let mut nearest_t = INFINITY;

        for prim in scene.octree.get_intersected_objects(self) {
            let intersection = prim.intersects(self, t_min, nearest_t);

            nearest_hit = match intersection {
                Some(intersection) => {
                    if intersection.t > t_min && intersection.t < nearest_t {
                        nearest_t = intersection.t;
                        Some(intersection)
                    } else {
                        nearest_hit
                    }
                },
                None => nearest_hit
            };
        }

        nearest_hit
    }

    pub fn perturb(&self, magnitude: f64) -> Ray {
        let rand_vec = Vec3::random() * magnitude;

        // Force random vectors to be in same direction as original vector
        let corrected_rand_vec = if rand_vec.dot(&self.direction) < 0.0 {
            rand_vec * -1.0
        } else {
            rand_vec
        };

        let direction = (corrected_rand_vec + self.direction).unit();

        Ray::new(self.origin, direction)
    }
}

#[test]
fn it_gets_the_nearest_hit() {
    let lights: Vec<Box<Light+Send+Sync>> = Vec::new();

    let mut prims: Vec<Box<Prim+Send+Sync>> = Vec::new();
    let mat = FlatMaterial { color: Vec3::one() };
    let sphere_top = Sphere {
        center: Vec3::zero(),
        radius: 1.0,
        material: Box::new(mat.clone()),
    };
    let sphere_mid = Sphere {
        center: Vec3 { x: -1.0, y: 0.0, z: 0.0 },
        radius: 1.0,
        material: Box::new(mat.clone()),
    };
    let sphere_bot = Sphere {
        center: Vec3 { x: -2.0, y: 0.0, z: 0.0 },
        radius: 1.0,
        material: Box::new(mat.clone()),
    };
    prims.push(Box::new(sphere_top));
    prims.push(Box::new(sphere_mid));
    prims.push(Box::new(sphere_bot));

    println!("Generating octree...");
    let octree = prims.into_iter().collect();
    println!("Octree generated...");

    let scene = Scene {
        lights: lights,
        background: Vec3::one(),
        octree: octree,
        skybox: None
    };

    let intersecting_ray = Ray::new(
        Vec3 { x: 10.0, y: 0.0, z: 0.0 },
        Vec3 { x: -1.0, y: 0.0, z: 0.0 }
    );

    let intersection = intersecting_ray.get_nearest_hit(&scene);
    assert_eq!(1.0, intersection.unwrap().position.x);

    let non_intersecting_ray = Ray::new(
        Vec3 { x: 10.0, y: 0.0, z: 0.0 },
        Vec3 { x: 1.0, y: 0.0, z: 0.0 });

    let non_intersection = non_intersecting_ray.get_nearest_hit(&scene);
    assert!(non_intersection.is_none());
}
<|endoftext|>


<commit_before>#[link(name = "rust_fuse",
uuid = "d37c5c30-fdcd-459d-bfca-ebb8da04b2a0",
url = "https://github.com/MicahChalmer/rust-fuse",
vers = "dev")];

#[comment = "FUSE bindings"];
#[license = "MIT"];
#[crate_type = "lib"];

use std::libc::{
    c_char,
    c_int,
    c_uint,
    c_ulong,
    c_void,
    mode_t,
    off_t,
    pid_t,
    size_t,
    stat,
    uid_t,
    gid_t
};

use std::ptr;
use std::str;
use std::vec;
use std::sys::size_of;
use std::cast;
use std::Result;

// A cfuncptr is used here to stand in for a C function pointer.
// For this struct, see fuse.h
type cfuncptr = *u8;
struct c_fuse_operations {
    getattr: cfuncptr,
    readlink: cfuncptr,
    getdir: cfuncptr,
    mknod: cfuncptr,
    mkdir: cfuncptr,
    unlink: cfuncptr,
    rmdir: cfuncptr,
    symlink: cfuncptr,
    rename: cfuncptr,
    link: cfuncptr,
    chmod: cfuncptr,
    chown: cfuncptr,
    truncate: cfuncptr,
    utime: cfuncptr,
    open: cfuncptr,
    read: cfuncptr,
    write: cfuncptr,
    statfs: cfuncptr,
    flush: cfuncptr,
    release: cfuncptr,
    fsync: cfuncptr,
    setxattr: cfuncptr,
    getxattr: cfuncptr,
    listxattr: cfuncptr,
    removexattr: cfuncptr,
    opendir: cfuncptr,
    readdir: cfuncptr,
    releasedir: cfuncptr,
    fsyncdir: cfuncptr,
    init: cfuncptr,
    destroy: cfuncptr,
    access: cfuncptr,
    create: cfuncptr,
    ftruncate: cfuncptr,
    fgetattr: cfuncptr,
    lock: cfuncptr,
    utimens: cfuncptr,
    bmap: cfuncptr,

    // flag_nullpath_ok, flag_nopath, flag_utime_omit_ok, flag_reserved
    flags: u32,

    ioctl: cfuncptr,
    poll: cfuncptr,
    write_buf: cfuncptr,
    read_buf: cfuncptr,
    flock: cfuncptr
}

// TODO: this should not be public
pub struct fuse_file_info {
    flags: c_int,
    fh_old: c_ulong, // Old file handle, don't use
    writepage: c_int,
    direct_io: c_uint,
    keep_cache: c_uint,
    flush: c_uint,
    nonseekable: c_uint,
    flock_release: c_uint,
    padding: c_uint, // Padding.  Do not use
    fh: u64,
    lock_owner: u64
}

struct c_fuse_context {
    fuse: *c_void,
    uid: uid_t,
    gid: gid_t,
    pid: pid_t,
    private_data: *rust_fuse_data,  // we use this to know what object to call
    umask: mode_t
}

struct rust_fuse_data {
    ops: ~FuseOperations
}

#[link_args = "-lfuse"]
extern {
    fn fuse_main_real(argc:c_int, argv:**c_char,
                      op:*c_fuse_operations, op_size: size_t,
                      user_data: *c_void) -> c_int;

    fn fuse_get_context() -> *c_fuse_context;
}

#[link_args = "-lfuserustwrapper -Lwrapper/lib"]
extern {

    // Workaround for the fact that we can't call into c via a function ptr
    // right from rust
    fn call_filler_function(filler: cfuncptr, buf: *c_void, name: *c_char,
                            stbuf: *stat, off: off_t) -> c_int;
}

// Used for return values from FS operations
pub type errno = c_int;

pub type fuse_fill_dir_func<'self> = &'self fn (&str, Option<stat>, off_t) -> c_int;

pub type filehandle = u64;

pub trait FuseOperations {
    fn getattr(&self, path:&str) -> Result<stat, errno>;
    fn readdir(&self, path:&str, filler: fuse_fill_dir_func,
               offset: off_t, info: &fuse_file_info) -> Result<(), errno>;
    fn open(&self, path:&str, info: &fuse_file_info) -> Result<filehandle, errno>;
    fn read(&self, path:&str, buf:&mut [u8], size: size_t, offset: off_t,
            info: &fuse_file_info) -> Result<c_int, errno>;
}

unsafe fn get_context_ops() -> &~FuseOperations {
    &((*(*fuse_get_context()).private_data).ops)
}

extern fn c_getattr(path: *c_char, stbuf: *mut stat) -> errno {
    unsafe {
        let ops = get_context_ops();
        ptr::zero_memory(stbuf, 1);
        match ops.getattr(str::raw::from_c_str(path)) {
            Err(e) => -e,
            Ok(st) => { ptr::copy_memory(stbuf, &st, 1); 0 }
        }
    }
}

unsafe fn option_to_ptr<T>(opt: Option<T>) -> *T {
    match opt {
        Some(ref t) => ptr::to_unsafe_ptr(t),
        None => ptr::null()
    }
}

extern fn c_readdir(path: *c_char, buf: *c_void, filler: cfuncptr,
                    offset: off_t, fi: *fuse_file_info) -> c_int {
    unsafe {
        let ops = get_context_ops();
        let fill_func: fuse_fill_dir_func = |name, st, ofs| -> c_int {
            do name.as_c_str |c_name| {
                call_filler_function(filler, buf, c_name, option_to_ptr(st), ofs)
            }
        };
        match ops.readdir(str::raw::from_c_str(path), fill_func, offset, &*fi) {
            Err(e) => -e,
            Ok(_) => 0
        }
    }
}

extern fn c_open(path: *c_char, info: *mut fuse_file_info) -> c_int {
    unsafe {
        let ops = get_context_ops();
        match ops.open(str::raw::from_c_str(path), &*info) {
            Err(e) => -e,
            Ok(fh) => { (*info).fh = fh; 0 }
        }
    }
}

extern fn c_read(path: *c_char, buf: *mut u8, size: size_t, offset: off_t,
                 fi: *fuse_file_info) -> c_int {
    unsafe {
        let ops = get_context_ops();
        do vec::raw::mut_buf_as_slice(buf, size as uint) |slice| {
            match ops.read(str::raw::from_c_str(path), slice, size, offset, &*fi) {
                Err(e) => -e,
                Ok(sz) => sz
            }
        }
    }
}

pub fn fuse_main(args: ~[~str], ops: ~FuseOperations) -> int {
    let cfo = c_fuse_operations {
        getattr: c_getattr,
        readdir: c_readdir,
        open: c_open,
        read: c_read,

        readlink: ptr::null(),
        getdir: ptr::null(),
        mknod: ptr::null(),
        mkdir: ptr::null(),
        unlink: ptr::null(),
        rmdir: ptr::null(),
        symlink: ptr::null(),
        rename: ptr::null(),
        link: ptr::null(),
        chmod: ptr::null(),
        chown: ptr::null(),
        truncate: ptr::null(),
        utime: ptr::null(),
        write: ptr::null(),
        statfs: ptr::null(),
        flush: ptr::null(),
        release: ptr::null(),
        fsync: ptr::null(),
        setxattr: ptr::null(),
        getxattr: ptr::null(),
        listxattr: ptr::null(),
        removexattr: ptr::null(),
        opendir: ptr::null(),
        releasedir: ptr::null(),
        fsyncdir: ptr::null(),
        init: ptr::null(),
        destroy: ptr::null(),
        access: ptr::null(),
        create: ptr::null(),
        ftruncate: ptr::null(),
        fgetattr: ptr::null(),
        lock: ptr::null(),
        utimens: ptr::null(),
        bmap: ptr::null(),
        flags:0,
        ioctl: ptr::null(),
        poll: ptr::null(),
        write_buf: ptr::null(),
        read_buf: ptr::null(),
        flock: ptr::null()
    };
    unsafe {
        let arg_c_strs_owner: ~[*u8] = args.map(|s| vec::raw::to_ptr(s.as_bytes_with_null()));
        let arg_c_strs_ptr = vec::raw::to_ptr(arg_c_strs_owner);
//        cast::forget(arg_c_strs_owner);

        fuse_main_real(args.len() as c_int, cast::transmute(arg_c_strs_ptr),
                       &cfo, size_of::<c_fuse_operations>() as size_t,
                       cast::transmute(~rust_fuse_data{ ops: ops }))  as int
    }
}
<commit_msg>Don't need the rust_fuse_data structure<commit_after>#[link(name = "rust_fuse",
uuid = "d37c5c30-fdcd-459d-bfca-ebb8da04b2a0",
url = "https://github.com/MicahChalmer/rust-fuse",
vers = "dev")];

#[comment = "FUSE bindings"];
#[license = "MIT"];
#[crate_type = "lib"];

use std::libc::{
    c_char,
    c_int,
    c_uint,
    c_ulong,
    c_void,
    mode_t,
    off_t,
    pid_t,
    size_t,
    stat,
    uid_t,
    gid_t
};

use std::ptr;
use std::str;
use std::vec;
use std::sys::size_of;
use std::cast;
use std::Result;

// A cfuncptr is used here to stand in for a C function pointer.
// For this struct, see fuse.h
type cfuncptr = *u8;
struct c_fuse_operations {
    getattr: cfuncptr,
    readlink: cfuncptr,
    getdir: cfuncptr,
    mknod: cfuncptr,
    mkdir: cfuncptr,
    unlink: cfuncptr,
    rmdir: cfuncptr,
    symlink: cfuncptr,
    rename: cfuncptr,
    link: cfuncptr,
    chmod: cfuncptr,
    chown: cfuncptr,
    truncate: cfuncptr,
    utime: cfuncptr,
    open: cfuncptr,
    read: cfuncptr,
    write: cfuncptr,
    statfs: cfuncptr,
    flush: cfuncptr,
    release: cfuncptr,
    fsync: cfuncptr,
    setxattr: cfuncptr,
    getxattr: cfuncptr,
    listxattr: cfuncptr,
    removexattr: cfuncptr,
    opendir: cfuncptr,
    readdir: cfuncptr,
    releasedir: cfuncptr,
    fsyncdir: cfuncptr,
    init: cfuncptr,
    destroy: cfuncptr,
    access: cfuncptr,
    create: cfuncptr,
    ftruncate: cfuncptr,
    fgetattr: cfuncptr,
    lock: cfuncptr,
    utimens: cfuncptr,
    bmap: cfuncptr,

    // flag_nullpath_ok, flag_nopath, flag_utime_omit_ok, flag_reserved
    flags: u32,

    ioctl: cfuncptr,
    poll: cfuncptr,
    write_buf: cfuncptr,
    read_buf: cfuncptr,
    flock: cfuncptr
}

// TODO: this should not be public
pub struct fuse_file_info {
    flags: c_int,
    fh_old: c_ulong, // Old file handle, don't use
    writepage: c_int,
    direct_io: c_uint,
    keep_cache: c_uint,
    flush: c_uint,
    nonseekable: c_uint,
    flock_release: c_uint,
    padding: c_uint, // Padding.  Do not use
    fh: u64,
    lock_owner: u64
}

struct c_fuse_context {
    fuse: *c_void,
    uid: uid_t,
    gid: gid_t,
    pid: pid_t,
    private_data: *~FuseOperations,  // we use this to know what object to call
    umask: mode_t
}

#[link_args = "-lfuse"]
extern {
    fn fuse_main_real(argc:c_int, argv:**c_char,
                      op:*c_fuse_operations, op_size: size_t,
                      user_data: *c_void) -> c_int;

    fn fuse_get_context() -> *c_fuse_context;
}

#[link_args = "-lfuserustwrapper -Lwrapper/lib"]
extern {

    // Workaround for the fact that we can't call into c via a function ptr
    // right from rust
    fn call_filler_function(filler: cfuncptr, buf: *c_void, name: *c_char,
                            stbuf: *stat, off: off_t) -> c_int;
}

// Used for return values from FS operations
pub type errno = c_int;

pub type fuse_fill_dir_func<'self> = &'self fn (&str, Option<stat>, off_t) -> c_int;

pub type filehandle = u64;

pub trait FuseOperations {
    fn getattr(&self, path:&str) -> Result<stat, errno>;
    fn readdir(&self, path:&str, filler: fuse_fill_dir_func,
               offset: off_t, info: &fuse_file_info) -> Result<(), errno>;
    fn open(&self, path:&str, info: &fuse_file_info) -> Result<filehandle, errno>;
    fn read(&self, path:&str, buf:&mut [u8], size: size_t, offset: off_t,
            info: &fuse_file_info) -> Result<c_int, errno>;
}

unsafe fn get_context_ops() -> &~FuseOperations {
    cast::transmute((*fuse_get_context()).private_data)
}

extern fn c_getattr(path: *c_char, stbuf: *mut stat) -> errno {
    unsafe {
        let ops = get_context_ops();
        ptr::zero_memory(stbuf, 1);
        match ops.getattr(str::raw::from_c_str(path)) {
            Err(e) => -e,
            Ok(st) => { ptr::copy_memory(stbuf, &st, 1); 0 }
        }
    }
}

unsafe fn option_to_ptr<T>(opt: Option<T>) -> *T {
    match opt {
        Some(ref t) => ptr::to_unsafe_ptr(t),
        None => ptr::null()
    }
}

extern fn c_readdir(path: *c_char, buf: *c_void, filler: cfuncptr,
                    offset: off_t, fi: *fuse_file_info) -> c_int {
    unsafe {
        let ops = get_context_ops();
        let fill_func: fuse_fill_dir_func = |name, st, ofs| -> c_int {
            do name.as_c_str |c_name| {
                call_filler_function(filler, buf, c_name, option_to_ptr(st), ofs)
            }
        };
        match ops.readdir(str::raw::from_c_str(path), fill_func, offset, &*fi) {
            Err(e) => -e,
            Ok(_) => 0
        }
    }
}

extern fn c_open(path: *c_char, info: *mut fuse_file_info) -> c_int {
    unsafe {
        let ops = get_context_ops();
        match ops.open(str::raw::from_c_str(path), &*info) {
            Err(e) => -e,
            Ok(fh) => { (*info).fh = fh; 0 }
        }
    }
}

extern fn c_read(path: *c_char, buf: *mut u8, size: size_t, offset: off_t,
                 fi: *fuse_file_info) -> c_int {
    unsafe {
        let ops = get_context_ops();
        do vec::raw::mut_buf_as_slice(buf, size as uint) |slice| {
            match ops.read(str::raw::from_c_str(path), slice, size, offset, &*fi) {
                Err(e) => -e,
                Ok(sz) => sz
            }
        }
    }
}

pub fn fuse_main(args: ~[~str], ops: ~FuseOperations) -> int {
    let cfo = c_fuse_operations {
        getattr: c_getattr,
        readdir: c_readdir,
        open: c_open,
        read: c_read,

        readlink: ptr::null(),
        getdir: ptr::null(),
        mknod: ptr::null(),
        mkdir: ptr::null(),
        unlink: ptr::null(),
        rmdir: ptr::null(),
        symlink: ptr::null(),
        rename: ptr::null(),
        link: ptr::null(),
        chmod: ptr::null(),
        chown: ptr::null(),
        truncate: ptr::null(),
        utime: ptr::null(),
        write: ptr::null(),
        statfs: ptr::null(),
        flush: ptr::null(),
        release: ptr::null(),
        fsync: ptr::null(),
        setxattr: ptr::null(),
        getxattr: ptr::null(),
        listxattr: ptr::null(),
        removexattr: ptr::null(),
        opendir: ptr::null(),
        releasedir: ptr::null(),
        fsyncdir: ptr::null(),
        init: ptr::null(),
        destroy: ptr::null(),
        access: ptr::null(),
        create: ptr::null(),
        ftruncate: ptr::null(),
        fgetattr: ptr::null(),
        lock: ptr::null(),
        utimens: ptr::null(),
        bmap: ptr::null(),
        flags:0,
        ioctl: ptr::null(),
        poll: ptr::null(),
        write_buf: ptr::null(),
        read_buf: ptr::null(),
        flock: ptr::null()
    };
    unsafe {
        let arg_c_strs_owner: ~[*u8] = args.map(|s| vec::raw::to_ptr(s.as_bytes_with_null()));
        let arg_c_strs_ptr = vec::raw::to_ptr(arg_c_strs_owner);
//        cast::forget(arg_c_strs_owner);

        fuse_main_real(args.len() as c_int, cast::transmute(arg_c_strs_ptr),
                       &cfo, size_of::<c_fuse_operations>() as size_t,
                       cast::transmute(&ops))  as int
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use serde::{Serialize, Serializer};
use std::{mem, ops, str};
use tree_sitter::{
    Language, Node, Parser, Query, QueryCursor, QueryError, QueryPredicateArg, Tree,
};

/// Contains the data neeeded to compute tags for code written in a
/// particular language.
pub struct TagsConfiguration {
    pub language: Language,
    pub query: Query,
    call_capture_index: Option<u32>,
    class_capture_index: Option<u32>,
    doc_capture_index: Option<u32>,
    function_capture_index: Option<u32>,
    locals_pattern_index: usize,
    method_capture_index: Option<u32>,
    module_capture_index: Option<u32>,
    name_capture_index: Option<u32>,
    pattern_info: Vec<PatternInfo>,
}

pub struct TagsContext {
    parser: Parser,
    cursor: QueryCursor,
}

#[derive(Default)]
struct PatternInfo {
    docs_adjacent_capture: Option<u32>,
    doc_strip_regex: Option<Regex>,
}

struct TagsIter<'a, I>
where
    I: Iterator<Item = tree_sitter::QueryMatch<'a>>,
{
    matches: I,
    _tree: Tree,
    source: &'a [u8],
    config: &'a TagsConfiguration,
    tag_queue: Vec<(Node<'a>, usize, Tag<'a>)>,
}

#[derive(Debug, Serialize, Clone)]
pub struct Loc {
    pub byte_range: ops::Range<usize>,
    pub span: ops::Range<Pos>,
}

#[derive(Debug, Serialize, Clone)]
pub struct Pos {
    pub line: i64,
    pub column: i64,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TagKind {
    Function,
    Method,
    Class,
    Module,
    Call,
}

#[derive(Debug, Serialize, Clone)]
pub struct Tag<'a> {
    pub kind: TagKind,
    pub name: &'a str,
    pub docs: Option<String>,
    pub loc: Loc,
    pub line: &'a str,
}

#[derive(Debug)]
pub enum Error {
    Query(QueryError),
    Regex(regex::Error),
}

impl TagsConfiguration {
    pub fn new(language: Language, tags_query: &str, locals_query: &str) -> Result<Self, Error> {
        let query = Query::new(language, &format!("{}{}", tags_query, locals_query))?;

        let locals_query_offset = tags_query.len();
        let mut locals_pattern_index = 0;
        for i in 0..(query.pattern_count()) {
            let pattern_offset = query.start_byte_for_pattern(i);
            if pattern_offset < locals_query_offset {
                locals_pattern_index += 1;
            }
        }

        let mut call_capture_index = None;
        let mut class_capture_index = None;
        let mut doc_capture_index = None;
        let mut function_capture_index = None;
        let mut method_capture_index = None;
        let mut module_capture_index = None;
        let mut name_capture_index = None;
        for (i, name) in query.capture_names().iter().enumerate() {
            let index = match name.as_str() {
                "call" => &mut call_capture_index,
                "class" => &mut class_capture_index,
                "doc" => &mut doc_capture_index,
                "function" => &mut function_capture_index,
                "method" => &mut method_capture_index,
                "module" => &mut module_capture_index,
                "name" => &mut name_capture_index,
                _ => continue,
            };
            *index = Some(i as u32);
        }

        let pattern_info = (0..query.pattern_count())
            .map(|pattern_index| {
                let mut info = PatternInfo::default();
                if let Some(doc_capture_index) = doc_capture_index {
                    for predicate in query.general_predicates(pattern_index) {
                        if predicate.args.get(0)
                            == Some(&QueryPredicateArg::Capture(doc_capture_index))
                        {
                            match (predicate.operator.as_ref(), predicate.args.get(1)) {
                                ("select-adjacent!", Some(QueryPredicateArg::Capture(index))) => {
                                    info.docs_adjacent_capture = Some(*index);
                                }
                                ("strip!", Some(QueryPredicateArg::String(pattern))) => {
                                    let regex = Regex::new(pattern.as_ref())?;
                                    info.doc_strip_regex = Some(regex);
                                }
                                _ => {}
                            }
                        }
                    }
                }
                return Ok(info);
            })
            .collect::<Result<Vec<_>, Error>>()?;

        Ok(TagsConfiguration {
            language,
            query,
            locals_pattern_index,
            function_capture_index,
            class_capture_index,
            method_capture_index,
            module_capture_index,
            doc_capture_index,
            call_capture_index,
            name_capture_index,
            pattern_info,
        })
    }
}

impl TagsContext {
    pub fn new() -> Self {
        TagsContext {
            parser: Parser::new(),
            cursor: QueryCursor::new(),
        }
    }

    // TODO: This should return an iterator rather than build up a vector
    pub fn generate_tags<'a>(
        &'a mut self,
        config: &'a TagsConfiguration,
        source: &'a [u8],
    ) -> impl Iterator<Item = Tag<'a>> + 'a {
        self.parser
            .set_language(config.language)
            .expect("Incompatible language");
        let tree = self
            .parser
            .parse(source, None)
            .expect("Parsing failed unexpectedly");

        // The `matches` iterator borrows the `Tree`, which prevents it from being moved.
        // But the tree is really just a pointer, so it's actually ok to move it.
        let tree_ref = unsafe { mem::transmute::<_, &'static Tree>(&tree) };
        let matches = self
            .cursor
            .matches(&config.query, tree_ref.root_node(), move |node| {
                &source[node.byte_range()]
            });
        TagsIter {
            matches,
            source,
            config,
            tag_queue: Vec::new(),
            _tree: tree,
        }
    }
}

impl<'a, I> Iterator for TagsIter<'a, I>
where
    I: Iterator<Item = tree_sitter::QueryMatch<'a>>,
{
    type Item = Tag<'a>;

    fn next(&mut self) -> Option<Tag<'a>> {
        loop {
            // If there is a queued tag for an earlier node in the syntax tree, then pop
            // it off of the queue and return it.
            if let Some(last_entry) = self.tag_queue.last() {
                if self.tag_queue.len() > 1
                    && self.tag_queue[0].0.end_byte() < last_entry.0.start_byte()
                {
                    return Some(self.tag_queue.remove(0).2);
                }
            }

            // If there is another match, then compute its tag and add it to the
            // tag queue.
            if let Some(mat) = self.matches.next() {
                let mut name = None;
                let mut doc_nodes = Vec::new();
                let mut tag_node = None;
                let mut tag_kind = TagKind::Call;
                let mut docs_adjacent_node = None;

                for capture in mat.captures {
                    let index = Some(capture.index);

                    if index == self.config.pattern_info[mat.pattern_index].docs_adjacent_capture {
                        docs_adjacent_node = Some(capture.node);
                    }

                    if index == self.config.name_capture_index {
                        name = str::from_utf8(&self.source[Some(capture.node)?.byte_range()]).ok();
                    } else if index == self.config.doc_capture_index {
                        doc_nodes.push(capture.node);
                    } else if index == self.config.call_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Call;
                    } else if index == self.config.class_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Class;
                    } else if index == self.config.function_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Function;
                    } else if index == self.config.method_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Method;
                    } else if index == self.config.module_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Module;
                    }
                }

                if let (Some(tag_node), Some(name)) = (tag_node, name) {
                    // If needed, filter the doc nodes based on their ranges, selecting
                    // only the slice that are adjacent to some specified node.
                    let mut docs_start_index = 0;
                    if let (Some(docs_adjacent_node), false) =
                        (docs_adjacent_node, doc_nodes.is_empty())
                    {
                        docs_start_index = doc_nodes.len();
                        let mut start_row = docs_adjacent_node.start_position().row;
                        while docs_start_index > 0 {
                            let doc_node = &doc_nodes[docs_start_index - 1];
                            let prev_doc_end_row = doc_node.end_position().row;
                            if prev_doc_end_row + 1 >= start_row {
                                docs_start_index -= 1;
                                start_row = doc_node.start_position().row;
                            } else {
                                break;
                            }
                        }
                    }

                    // Generate a doc string from all of the doc nodes, applying any strip regexes.
                    let mut docs = None;
                    for doc_node in &doc_nodes[docs_start_index..] {
                        if let Ok(content) = str::from_utf8(&self.source[doc_node.byte_range()]) {
                            let content = if let Some(regex) =
                                &self.config.pattern_info[mat.pattern_index].doc_strip_regex
                            {
                                regex.replace_all(content, "").to_string()
                            } else {
                                content.to_string()
                            };
                            match &mut docs {
                                None => docs = Some(content),
                                Some(d) => {
                                    d.push('\n');
                                    d.push_str(&content);
                                }
                            }
                        }
                    }

                    let source = &self.source;
                    let tag_from_node = |node: Node, kind: TagKind| -> Option<Tag> {
                        // Slice out the first line of the text corresponding to the node in question.
                        let mut line_range = node.byte_range();
                        line_range.end = line_range.end.min(line_range.start + 180);
                        let line = str::from_utf8(&source[line_range]).ok()?.lines().next()?;
                        Some(Tag {
                            name,
                            line,
                            kind,
                            docs,
                            loc: loc_for_node(node),
                        })
                    };

                    // Only create one tag per node. The tag queue is sorted by node position
                    // to allow for fast lookup.
                    match self.tag_queue.binary_search_by_key(
                        &(tag_node.end_byte(), tag_node.start_byte(), tag_node.id()),
                        |(node, _, _)| (node.end_byte(), node.start_byte(), node.id()),
                    ) {
                        Ok(i) => {
                            let (_, old_idx, tag) = &mut self.tag_queue[i];
                            if *old_idx > mat.pattern_index {
                                if let Some(new_tag) = tag_from_node(tag_node, tag_kind) {
                                    *tag = new_tag;
                                    *old_idx = mat.pattern_index;
                                }
                            }
                        }
                        Err(i) => {
                            if let Some(tag) = tag_from_node(tag_node, tag_kind) {
                                self.tag_queue.insert(i, (tag_node, mat.pattern_index, tag))
                            }
                        }
                    }
                }
            }
            // If there are no more matches, then drain the queue.
            else if !self.tag_queue.is_empty() {
                return Some(self.tag_queue.remove(0).2);
            } else {
                return None;
            }
        }
    }
}

impl Serialize for TagKind {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            TagKind::Call => "Call",
            TagKind::Module => "Module",
            TagKind::Class => "Class",
            TagKind::Method => "Method",
            TagKind::Function => "Function",
        }
        .serialize(s)
    }
}

fn loc_for_node(node: Node) -> Loc {
    Loc {
        byte_range: node.byte_range(),
        span: node.start_position().into()..node.start_position().into(),
    }
}

impl From<tree_sitter::Point> for Pos {
    fn from(point: tree_sitter::Point) -> Self {
        return Pos {
            line: point.row as i64,
            column: point.column as i64,
        };
    }
}

impl From<regex::Error> for Error {
    fn from(error: regex::Error) -> Self {
        Error::Regex(error)
    }
}

impl From<QueryError> for Error {
    fn from(error: QueryError) -> Self {
        Error::Query(error)
    }
}
<commit_msg>Suppress unused field warning<commit_after>use regex::Regex;
use serde::{Serialize, Serializer};
use std::{mem, ops, str};
use tree_sitter::{
    Language, Node, Parser, Query, QueryCursor, QueryError, QueryPredicateArg, Tree,
};

/// Contains the data neeeded to compute tags for code written in a
/// particular language.
pub struct TagsConfiguration {
    pub language: Language,
    pub query: Query,
    call_capture_index: Option<u32>,
    class_capture_index: Option<u32>,
    doc_capture_index: Option<u32>,
    function_capture_index: Option<u32>,
    method_capture_index: Option<u32>,
    module_capture_index: Option<u32>,
    name_capture_index: Option<u32>,
    pattern_info: Vec<PatternInfo>,
    _locals_pattern_index: usize,
}

pub struct TagsContext {
    parser: Parser,
    cursor: QueryCursor,
}

#[derive(Default)]
struct PatternInfo {
    docs_adjacent_capture: Option<u32>,
    doc_strip_regex: Option<Regex>,
}

struct TagsIter<'a, I>
where
    I: Iterator<Item = tree_sitter::QueryMatch<'a>>,
{
    matches: I,
    _tree: Tree,
    source: &'a [u8],
    config: &'a TagsConfiguration,
    tag_queue: Vec<(Node<'a>, usize, Tag<'a>)>,
}

#[derive(Debug, Serialize, Clone)]
pub struct Loc {
    pub byte_range: ops::Range<usize>,
    pub span: ops::Range<Pos>,
}

#[derive(Debug, Serialize, Clone)]
pub struct Pos {
    pub line: i64,
    pub column: i64,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TagKind {
    Function,
    Method,
    Class,
    Module,
    Call,
}

#[derive(Debug, Serialize, Clone)]
pub struct Tag<'a> {
    pub kind: TagKind,
    pub name: &'a str,
    pub docs: Option<String>,
    pub loc: Loc,
    pub line: &'a str,
}

#[derive(Debug)]
pub enum Error {
    Query(QueryError),
    Regex(regex::Error),
}

impl TagsConfiguration {
    pub fn new(language: Language, tags_query: &str, locals_query: &str) -> Result<Self, Error> {
        let query = Query::new(language, &format!("{}{}", tags_query, locals_query))?;

        let locals_query_offset = tags_query.len();
        let mut locals_pattern_index = 0;
        for i in 0..(query.pattern_count()) {
            let pattern_offset = query.start_byte_for_pattern(i);
            if pattern_offset < locals_query_offset {
                locals_pattern_index += 1;
            }
        }

        let mut call_capture_index = None;
        let mut class_capture_index = None;
        let mut doc_capture_index = None;
        let mut function_capture_index = None;
        let mut method_capture_index = None;
        let mut module_capture_index = None;
        let mut name_capture_index = None;
        for (i, name) in query.capture_names().iter().enumerate() {
            let index = match name.as_str() {
                "call" => &mut call_capture_index,
                "class" => &mut class_capture_index,
                "doc" => &mut doc_capture_index,
                "function" => &mut function_capture_index,
                "method" => &mut method_capture_index,
                "module" => &mut module_capture_index,
                "name" => &mut name_capture_index,
                _ => continue,
            };
            *index = Some(i as u32);
        }

        let pattern_info = (0..query.pattern_count())
            .map(|pattern_index| {
                let mut info = PatternInfo::default();
                if let Some(doc_capture_index) = doc_capture_index {
                    for predicate in query.general_predicates(pattern_index) {
                        if predicate.args.get(0)
                            == Some(&QueryPredicateArg::Capture(doc_capture_index))
                        {
                            match (predicate.operator.as_ref(), predicate.args.get(1)) {
                                ("select-adjacent!", Some(QueryPredicateArg::Capture(index))) => {
                                    info.docs_adjacent_capture = Some(*index);
                                }
                                ("strip!", Some(QueryPredicateArg::String(pattern))) => {
                                    let regex = Regex::new(pattern.as_ref())?;
                                    info.doc_strip_regex = Some(regex);
                                }
                                _ => {}
                            }
                        }
                    }
                }
                return Ok(info);
            })
            .collect::<Result<Vec<_>, Error>>()?;

        Ok(TagsConfiguration {
            language,
            query,
            function_capture_index,
            class_capture_index,
            method_capture_index,
            module_capture_index,
            doc_capture_index,
            call_capture_index,
            name_capture_index,
            pattern_info,
            _locals_pattern_index: locals_pattern_index,
        })
    }
}

impl TagsContext {
    pub fn new() -> Self {
        TagsContext {
            parser: Parser::new(),
            cursor: QueryCursor::new(),
        }
    }

    // TODO: This should return an iterator rather than build up a vector
    pub fn generate_tags<'a>(
        &'a mut self,
        config: &'a TagsConfiguration,
        source: &'a [u8],
    ) -> impl Iterator<Item = Tag<'a>> + 'a {
        self.parser
            .set_language(config.language)
            .expect("Incompatible language");
        let tree = self
            .parser
            .parse(source, None)
            .expect("Parsing failed unexpectedly");

        // The `matches` iterator borrows the `Tree`, which prevents it from being moved.
        // But the tree is really just a pointer, so it's actually ok to move it.
        let tree_ref = unsafe { mem::transmute::<_, &'static Tree>(&tree) };
        let matches = self
            .cursor
            .matches(&config.query, tree_ref.root_node(), move |node| {
                &source[node.byte_range()]
            });
        TagsIter {
            matches,
            source,
            config,
            tag_queue: Vec::new(),
            _tree: tree,
        }
    }
}

impl<'a, I> Iterator for TagsIter<'a, I>
where
    I: Iterator<Item = tree_sitter::QueryMatch<'a>>,
{
    type Item = Tag<'a>;

    fn next(&mut self) -> Option<Tag<'a>> {
        loop {
            // If there is a queued tag for an earlier node in the syntax tree, then pop
            // it off of the queue and return it.
            if let Some(last_entry) = self.tag_queue.last() {
                if self.tag_queue.len() > 1
                    && self.tag_queue[0].0.end_byte() < last_entry.0.start_byte()
                {
                    return Some(self.tag_queue.remove(0).2);
                }
            }

            // If there is another match, then compute its tag and add it to the
            // tag queue.
            if let Some(mat) = self.matches.next() {
                let mut name = None;
                let mut doc_nodes = Vec::new();
                let mut tag_node = None;
                let mut tag_kind = TagKind::Call;
                let mut docs_adjacent_node = None;

                for capture in mat.captures {
                    let index = Some(capture.index);

                    if index == self.config.pattern_info[mat.pattern_index].docs_adjacent_capture {
                        docs_adjacent_node = Some(capture.node);
                    }

                    if index == self.config.name_capture_index {
                        name = str::from_utf8(&self.source[Some(capture.node)?.byte_range()]).ok();
                    } else if index == self.config.doc_capture_index {
                        doc_nodes.push(capture.node);
                    } else if index == self.config.call_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Call;
                    } else if index == self.config.class_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Class;
                    } else if index == self.config.function_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Function;
                    } else if index == self.config.method_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Method;
                    } else if index == self.config.module_capture_index {
                        tag_node = Some(capture.node);
                        tag_kind = TagKind::Module;
                    }
                }

                if let (Some(tag_node), Some(name)) = (tag_node, name) {
                    // If needed, filter the doc nodes based on their ranges, selecting
                    // only the slice that are adjacent to some specified node.
                    let mut docs_start_index = 0;
                    if let (Some(docs_adjacent_node), false) =
                        (docs_adjacent_node, doc_nodes.is_empty())
                    {
                        docs_start_index = doc_nodes.len();
                        let mut start_row = docs_adjacent_node.start_position().row;
                        while docs_start_index > 0 {
                            let doc_node = &doc_nodes[docs_start_index - 1];
                            let prev_doc_end_row = doc_node.end_position().row;
                            if prev_doc_end_row + 1 >= start_row {
                                docs_start_index -= 1;
                                start_row = doc_node.start_position().row;
                            } else {
                                break;
                            }
                        }
                    }

                    // Generate a doc string from all of the doc nodes, applying any strip regexes.
                    let mut docs = None;
                    for doc_node in &doc_nodes[docs_start_index..] {
                        if let Ok(content) = str::from_utf8(&self.source[doc_node.byte_range()]) {
                            let content = if let Some(regex) =
                                &self.config.pattern_info[mat.pattern_index].doc_strip_regex
                            {
                                regex.replace_all(content, "").to_string()
                            } else {
                                content.to_string()
                            };
                            match &mut docs {
                                None => docs = Some(content),
                                Some(d) => {
                                    d.push('\n');
                                    d.push_str(&content);
                                }
                            }
                        }
                    }

                    let source = &self.source;
                    let tag_from_node = |node: Node, kind: TagKind| -> Option<Tag> {
                        // Slice out the first line of the text corresponding to the node in question.
                        let mut line_range = node.byte_range();
                        line_range.end = line_range.end.min(line_range.start + 180);
                        let line = str::from_utf8(&source[line_range]).ok()?.lines().next()?;
                        Some(Tag {
                            name,
                            line,
                            kind,
                            docs,
                            loc: loc_for_node(node),
                        })
                    };

                    // Only create one tag per node. The tag queue is sorted by node position
                    // to allow for fast lookup.
                    match self.tag_queue.binary_search_by_key(
                        &(tag_node.end_byte(), tag_node.start_byte(), tag_node.id()),
                        |(node, _, _)| (node.end_byte(), node.start_byte(), node.id()),
                    ) {
                        Ok(i) => {
                            let (_, old_idx, tag) = &mut self.tag_queue[i];
                            if *old_idx > mat.pattern_index {
                                if let Some(new_tag) = tag_from_node(tag_node, tag_kind) {
                                    *tag = new_tag;
                                    *old_idx = mat.pattern_index;
                                }
                            }
                        }
                        Err(i) => {
                            if let Some(tag) = tag_from_node(tag_node, tag_kind) {
                                self.tag_queue.insert(i, (tag_node, mat.pattern_index, tag))
                            }
                        }
                    }
                }
            }
            // If there are no more matches, then drain the queue.
            else if !self.tag_queue.is_empty() {
                return Some(self.tag_queue.remove(0).2);
            } else {
                return None;
            }
        }
    }
}

impl Serialize for TagKind {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            TagKind::Call => "Call",
            TagKind::Module => "Module",
            TagKind::Class => "Class",
            TagKind::Method => "Method",
            TagKind::Function => "Function",
        }
        .serialize(s)
    }
}

fn loc_for_node(node: Node) -> Loc {
    Loc {
        byte_range: node.byte_range(),
        span: node.start_position().into()..node.start_position().into(),
    }
}

impl From<tree_sitter::Point> for Pos {
    fn from(point: tree_sitter::Point) -> Self {
        return Pos {
            line: point.row as i64,
            column: point.column as i64,
        };
    }
}

impl From<regex::Error> for Error {
    fn from(error: regex::Error) -> Self {
        Error::Regex(error)
    }
}

impl From<QueryError> for Error {
    fn from(error: QueryError) -> Self {
        Error::Query(error)
    }
}
<|endoftext|>


<commit_before>//! This is the Buffer module for Xtensis.

// This file is part of Xtensis.

// This is the Xtensis text editor; it edits text.
// Copyright (C) 2016-2017  The Xtensis Developers

// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see
// <http://www.gnu.org/licenses/>.

extern crate slog;

use std::path::PathBuf;

use server::gapbuffer::GapBuffer;
use utils::types::BufferID;
use utils::uuid::get_uuid_buffer;

/// Struct for a Buffer in Xtensis
/// This stores metadata about a buffer.
#[derive(Debug)]
pub struct Buffer {
    /// UUID of a buffer.
    pub buffer_uuid: BufferID,
    /// File path of a buffer.
    pub file_path: Option<PathBuf>,
    /// Active status of a buffer.
    pub active: bool,
    /// Temporary status of a buffer.
    pub temporary: bool,
    /// If a buffer is r/o.
    /// If this is `true`, then r/w is `false.`
    /// Likewise, if this is `false`, then r/w is `true.
    pub read_only: bool,
    /// Major mode of a buffer
    pub major_mode: &'static str,
    /// Vector of Minor modes in a buffer
    pub minor_modes: Vec<&'static str>,
    /// Dirty status of a buffer
    pub dirty: bool,
    /// Contents of a buffer.
    pub text: GapBuffer<u8>,
}

impl Buffer {
    /// Return a new instance of `Buffer`.
    pub fn new() -> Buffer {
        Buffer {
            buffer_uuid: get_uuid_buffer(),
            file_path: None,
            active: false,
            temporary: false,
            read_only: false,
            major_mode: "fundamental-mode",
            minor_modes: Vec::new(),
            dirty: false,
            text: GapBuffer::new(),
        }
    }

    /// Return true if a buffer is active.
    /// That is to say, a 'focused' buffer, or otherwise in-use, and
    /// *not* inactive.
    /// If it's not a active buffer, then return false.
    pub fn is_active(&self) -> bool {
        self.active
    }

    /// Return true if a buffer is temporary.
    /// If it's not a temporary buffer, then return false.
    /// A temporary buffer is defined by the characteristic of having
    /// no 'target' to save to.
    pub fn is_temporary(&self) -> bool {
        self.temporary
    }

    /// Return true if a buffer is read only.

    /// If it's not read-only, then it returns false, and the buffer
    /// will be presumed to be read-write.

    /// A read-only buffer is defined by the characteristic of the FS
    /// denying write access, or being set manually.
    pub fn is_ro(&self) -> bool {
        self.read_only
    }

    /// Return true if a buffer is dirty.
    /// If it's not dirty, then return false.
    /// A dirty buffer is defined by the characteristic of the buffer
    /// having changed since the last save.
    /// Temporary buffers are *exempt* from this field.
    pub fn is_dirty(&self) -> bool {
        self.dirty
    }

    /// Return the current major mode of a buffer.
    pub fn get_major_mode(&self) -> &str {
        self.major_mode
    }

    /// Return a `Vec<&'static str>` of minor modes.
    pub fn get_minor_modes(&self) -> Vec<&'static str> {
        self.minor_modes.clone()
    }

    /// Return the length of a buffer.
    pub fn get_buffer_length(&self) -> usize {
        self.text.len()
    }
}

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

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

    #[test]
    fn test_buffer_default_values() {
        let buf = Buffer::new();

        assert_eq!(None, buf.file_path);
        assert_eq!(false, buf.active);
        assert_eq!(false, buf.is_active());

        assert_eq!(false, buf.temporary);
        assert_eq!(false, buf.is_temporary());

        assert_eq!(false, buf.read_only);
        assert_eq!(false, buf.is_ro());

        assert_eq!("fundamental-mode", buf.major_mode);
        assert_eq!(false, buf.dirty);
    }

    #[test]
    fn test_buffer_major_minor_modes() {
        let mut buf = Buffer::new();
        buf.major_mode = "rust-mode";
        buf.minor_modes.push("auto-wrap-mode");
        buf.minor_modes.push("spell-check-mode");
        buf.minor_modes.push("complete-me-mode");
        buf.minor_modes.push("language-server-mode");

        assert_eq!(4, buf.minor_modes.len());
        assert_eq!("rust-mode", buf.major_mode);
    }
}
<commit_msg>Add logging to Buffer module<commit_after>//! This is the Buffer module for Xtensis.

// This file is part of Xtensis.

// This is the Xtensis text editor; it edits text.
// Copyright (C) 2016-2017  The Xtensis Developers

// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see
// <http://www.gnu.org/licenses/>.

extern crate slog;

use logging::init_logger;
use server::gapbuffer::GapBuffer;
use std::path::PathBuf;
use utils::types::BufferID;
use utils::uuid::get_uuid_buffer;

/// Struct for a Buffer in Xtensis
/// This stores metadata about a buffer.
#[derive(Debug)]
pub struct Buffer {
    /// UUID of a buffer.
    pub buffer_uuid: BufferID,
    /// File path of a buffer.
    pub file_path: Option<PathBuf>,
    /// Active status of a buffer.
    pub active: bool,
    /// Temporary status of a buffer.
    pub temporary: bool,
    /// If a buffer is r/o.
    /// If this is `true`, then r/w is `false.`
    /// Likewise, if this is `false`, then r/w is `true.
    pub read_only: bool,
    /// Major mode of a buffer
    pub major_mode: &'static str,
    /// Vector of Minor modes in a buffer
    pub minor_modes: Vec<&'static str>,
    /// Dirty status of a buffer
    pub dirty: bool,
    /// Contents of a buffer.
    pub text: GapBuffer<u8>,
}

impl Buffer {
    /// Return a new instance of `Buffer`.
    pub fn new() -> Buffer {
        let buffer_uuid = get_uuid_buffer();

        let logger = init_logger().new(o!("buffer_uuid" =>
                                          buffer_uuid.clone()));
        Buffer {
            buffer_uuid: buffer_uuid,
            file_path: None,
            active: false,
            temporary: false,
            read_only: false,
            major_mode: "fundamental-mode",
            minor_modes: Vec::new(),
            dirty: false,
            text: GapBuffer::new(),
        }
    }

    /// Return true if a buffer is active.
    /// That is to say, a 'focused' buffer, or otherwise in-use, and
    /// *not* inactive.
    /// If it's not a active buffer, then return false.
    pub fn is_active(&self) -> bool {
        self.active
    }

    /// Return true if a buffer is temporary.
    /// If it's not a temporary buffer, then return false.
    /// A temporary buffer is defined by the characteristic of having
    /// no 'target' to save to.
    pub fn is_temporary(&self) -> bool {
        self.temporary
    }

    /// Return true if a buffer is read only.

    /// If it's not read-only, then it returns false, and the buffer
    /// will be presumed to be read-write.

    /// A read-only buffer is defined by the characteristic of the FS
    /// denying write access, or being set manually.
    pub fn is_ro(&self) -> bool {
        self.read_only
    }

    /// Return true if a buffer is dirty.
    /// If it's not dirty, then return false.
    /// A dirty buffer is defined by the characteristic of the buffer
    /// having changed since the last save.
    /// Temporary buffers are *exempt* from this field.
    pub fn is_dirty(&self) -> bool {
        self.dirty
    }

    /// Return the current major mode of a buffer.
    pub fn get_major_mode(&self) -> &str {
        self.major_mode
    }

    /// Return a `Vec<&'static str>` of minor modes.
    pub fn get_minor_modes(&self) -> Vec<&'static str> {
        self.minor_modes.clone()
    }

    /// Return the length of a buffer.
    pub fn get_buffer_length(&self) -> usize {
        self.text.len()
    }
}

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

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

    #[test]
    fn test_buffer_default_values() {
        let buf = Buffer::new();

        assert_eq!(None, buf.file_path);
        assert_eq!(false, buf.active);
        assert_eq!(false, buf.is_active());

        assert_eq!(false, buf.temporary);
        assert_eq!(false, buf.is_temporary());

        assert_eq!(false, buf.read_only);
        assert_eq!(false, buf.is_ro());

        assert_eq!("fundamental-mode", buf.major_mode);
        assert_eq!(false, buf.dirty);
    }

    #[test]
    fn test_buffer_major_minor_modes() {
        let mut buf = Buffer::new();
        buf.major_mode = "rust-mode";
        buf.minor_modes.push("auto-wrap-mode");
        buf.minor_modes.push("spell-check-mode");
        buf.minor_modes.push("complete-me-mode");
        buf.minor_modes.push("language-server-mode");

        assert_eq!(4, buf.minor_modes.len());
        assert_eq!("rust-mode", buf.major_mode);
    }
}
<|endoftext|>


<commit_before>mod cli;

use std::{
    collections::HashMap,
    path::{Path, PathBuf},
};

use xtask::{not_bash::fs2, project_root, rust_files};

#[test]
fn rust_files_are_tidy() {
    let mut tidy_docs = TidyDocs::default();
    for path in rust_files(&project_root().join("crates")) {
        let text = fs2::read_to_string(&path).unwrap();
        check_todo(&path, &text);
        check_trailing_ws(&path, &text);
        tidy_docs.visit(&path, &text);
    }
    tidy_docs.finish();
}

fn check_todo(path: &Path, text: &str) {
    let whitelist = &[
        // This file itself is whitelisted since this test itself contains matches.
        "tests/cli.rs",
        // Some of our assists generate `todo!()` so those files are whitelisted.
        "doc_tests/generated.rs",
        "handlers/add_missing_impl_members.rs",
        "handlers/add_function.rs",
        // To support generating `todo!()` in assists, we have `expr_todo()` in ast::make.
        "ast/make.rs",
    ];
    if whitelist.iter().any(|p| path.ends_with(p)) {
        return;
    }
    if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
        panic!(
            "\nTODO markers should not be committed to the master branch,\n\
             use FIXME instead\n\
             {}\n",
            path.display(),
        )
    }
}

fn check_trailing_ws(path: &Path, text: &str) {
    if is_exclude_dir(path, &["test_data"]) {
        return;
    }
    for line in text.lines() {
        if line.chars().last().map(char::is_whitespace) == Some(true) {
            panic!("Trailing whitespace in {}", path.display())
        }
    }
}

#[derive(Default)]
struct TidyDocs {
    missing_docs: Vec<String>,
    contains_fixme: Vec<PathBuf>,
}

impl TidyDocs {
    fn visit(&mut self, path: &Path, text: &str) {
        // Test hopefully don't really need comments, and for assists we already
        // have special comments which are source of doc tests and user docs.
        if is_exclude_dir(path, &["tests", "test_data", "handlers"]) {
            return;
        }

        if is_exclude_file(path) {
            return;
        }

        let first_line = match text.lines().next() {
            Some(it) => it,
            None => return,
        };

        if first_line.starts_with("//!") {
            if first_line.contains("FIXME") {
                self.contains_fixme.push(path.to_path_buf())
            }
        } else {
            self.missing_docs.push(path.display().to_string());
        }

        fn is_exclude_file(d: &Path) -> bool {
            let file_names = ["tests.rs"];

            d.file_name()
                .unwrap_or_default()
                .to_str()
                .map(|f_n| file_names.iter().any(|name| *name == f_n))
                .unwrap_or(false)
        }
    }

    fn finish(self) {
        if !self.missing_docs.is_empty() {
            panic!(
                "\nMissing docs strings\n\n\
                 modules:\n{}\n\n",
                self.missing_docs.join("\n")
            )
        }

        let whitelist = [
            "ra_db",
            "ra_hir",
            "ra_hir_expand",
            "ra_ide",
            "ra_mbe",
            "ra_parser",
            "ra_prof",
            "ra_project_model",
            "ra_syntax",
            "ra_text_edit",
            "ra_tt",
            "ra_hir_ty",
        ];

        let mut has_fixmes =
            whitelist.iter().map(|it| (*it, false)).collect::<HashMap<&str, bool>>();
        'outer: for path in self.contains_fixme {
            for krate in whitelist.iter() {
                if path.components().any(|it| it.as_os_str() == *krate) {
                    has_fixmes.insert(krate, true);
                    continue 'outer;
                }
            }
            panic!("FIXME doc in a fully-documented crate: {}", path.display())
        }

        for (krate, has_fixme) in has_fixmes.iter() {
            if !has_fixme {
                panic!("crate {} is fully documented, remove it from the white list", krate)
            }
        }
    }
}

fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool {
    let mut cur_path = p;
    while let Some(path) = cur_path.parent() {
        if dirs_to_exclude.iter().any(|dir| path.ends_with(dir)) {
            return true;
        }
        cur_path = path;
    }

    false
}
<commit_msg>tests: add more info about what failed in tidy tests<commit_after>mod cli;

use std::{
    collections::HashMap,
    path::{Path, PathBuf},
};

use xtask::{not_bash::fs2, project_root, rust_files};

#[test]
fn rust_files_are_tidy() {
    let mut tidy_docs = TidyDocs::default();
    for path in rust_files(&project_root().join("crates")) {
        let text = fs2::read_to_string(&path).unwrap();
        check_todo(&path, &text);
        check_trailing_ws(&path, &text);
        tidy_docs.visit(&path, &text);
    }
    tidy_docs.finish();
}

fn check_todo(path: &Path, text: &str) {
    let whitelist = &[
        // This file itself is whitelisted since this test itself contains matches.
        "tests/cli.rs",
        // Some of our assists generate `todo!()` so those files are whitelisted.
        "doc_tests/generated.rs",
        "handlers/add_missing_impl_members.rs",
        "handlers/add_function.rs",
        // To support generating `todo!()` in assists, we have `expr_todo()` in ast::make.
        "ast/make.rs",
    ];
    if whitelist.iter().any(|p| path.ends_with(p)) {
        return;
    }
    if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
        panic!(
            "\nTODO markers or todo! macros should not be committed to the master branch,\n\
             use FIXME instead\n\
             {}\n",
            path.display(),
        )
    }
}

fn check_trailing_ws(path: &Path, text: &str) {
    if is_exclude_dir(path, &["test_data"]) {
        return;
    }
    for (line_number, line) in text.lines().enumerate() {
        if line.chars().last().map(char::is_whitespace) == Some(true) {
            panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
        }
    }
}

#[derive(Default)]
struct TidyDocs {
    missing_docs: Vec<String>,
    contains_fixme: Vec<PathBuf>,
}

impl TidyDocs {
    fn visit(&mut self, path: &Path, text: &str) {
        // Test hopefully don't really need comments, and for assists we already
        // have special comments which are source of doc tests and user docs.
        if is_exclude_dir(path, &["tests", "test_data", "handlers"]) {
            return;
        }

        if is_exclude_file(path) {
            return;
        }

        let first_line = match text.lines().next() {
            Some(it) => it,
            None => return,
        };

        if first_line.starts_with("//!") {
            if first_line.contains("FIXME") {
                self.contains_fixme.push(path.to_path_buf())
            }
        } else {
            self.missing_docs.push(path.display().to_string());
        }

        fn is_exclude_file(d: &Path) -> bool {
            let file_names = ["tests.rs"];

            d.file_name()
                .unwrap_or_default()
                .to_str()
                .map(|f_n| file_names.iter().any(|name| *name == f_n))
                .unwrap_or(false)
        }
    }

    fn finish(self) {
        if !self.missing_docs.is_empty() {
            panic!(
                "\nMissing docs strings\n\n\
                 modules:\n{}\n\n",
                self.missing_docs.join("\n")
            )
        }

        let whitelist = [
            "ra_db",
            "ra_hir",
            "ra_hir_expand",
            "ra_ide",
            "ra_mbe",
            "ra_parser",
            "ra_prof",
            "ra_project_model",
            "ra_syntax",
            "ra_text_edit",
            "ra_tt",
            "ra_hir_ty",
        ];

        let mut has_fixmes =
            whitelist.iter().map(|it| (*it, false)).collect::<HashMap<&str, bool>>();
        'outer: for path in self.contains_fixme {
            for krate in whitelist.iter() {
                if path.components().any(|it| it.as_os_str() == *krate) {
                    has_fixmes.insert(krate, true);
                    continue 'outer;
                }
            }
            panic!("FIXME doc in a fully-documented crate: {}", path.display())
        }

        for (krate, has_fixme) in has_fixmes.iter() {
            if !has_fixme {
                panic!("crate {} is fully documented, remove it from the white list", krate)
            }
        }
    }
}

fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool {
    let mut cur_path = p;
    while let Some(path) = cur_path.parent() {
        if dirs_to_exclude.iter().any(|dir| path.ends_with(dir)) {
            return true;
        }
        cur_path = path;
    }

    false
}
<|endoftext|>