<commit_before>use nix::sys::uio::*;
use nix::unistd::*;
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
use std::{cmp, iter};
use std::fs::{OpenOptions};
use std::os::unix::io::AsRawFd;

#[cfg(not(target_os = "redox"))]
use tempfile::tempfile;
use tempfile::tempdir;

#[test]
fn test_writev() {
    let mut to_write = Vec::with_capacity(16 * 128);
    for _ in 0..16 {
        let s: String = thread_rng()
            .sample_iter(&Alphanumeric)
            .map(char::from)
            .take(128)
            .collect();
        let b = s.as_bytes();
        to_write.extend(b.iter().cloned());
    }
    // Allocate and fill iovecs
    let mut iovecs = Vec::new();
    let mut consumed = 0;
    while consumed < to_write.len() {
        let left = to_write.len() - consumed;
        let slice_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) };
        let b = &to_write[consumed..consumed+slice_len];
        iovecs.push(IoVec::from_slice(b));
        consumed += slice_len;
    }
    let pipe_res = pipe();
    assert!(pipe_res.is_ok());
    let (reader, writer) = pipe_res.ok().unwrap();
    // FileDesc will close its filedesc (reader).
    let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
    // Blocking io, should write all data.
    let write_res = writev(writer, &iovecs);
    // Successful write
    assert!(write_res.is_ok());
    let written = write_res.ok().unwrap();
    // Check whether we written all data
    assert_eq!(to_write.len(), written);
    let read_res = read(reader, &mut read_buf[..]);
    // Successful read
    assert!(read_res.is_ok());
    let read = read_res.ok().unwrap() as usize;
    // Check we have read as much as we written
    assert_eq!(read, written);
    // Check equality of written and read data
    assert_eq!(&to_write, &read_buf);
    let close_res = close(writer);
    assert!(close_res.is_ok());
    let close_res = close(reader);
    assert!(close_res.is_ok());
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_readv() {
    let s:String = thread_rng()
        .sample_iter(&Alphanumeric)
        .map(char::from)
        .take(128)
        .collect();
    let to_write = s.as_bytes().to_vec();
    let mut storage = Vec::new();
    let mut allocated = 0;
    while allocated < to_write.len() {
        let left = to_write.len() - allocated;
        let vec_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) };
        let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
        storage.push(v);
        allocated += vec_len;
    }
    let mut iovecs = Vec::with_capacity(storage.len());
    for v in &mut storage {
        iovecs.push(IoVec::from_mut_slice(&mut v[..]));
    }
    let pipe_res = pipe();
    assert!(pipe_res.is_ok());
    let (reader, writer) = pipe_res.ok().unwrap();
    // Blocking io, should write all data.
    let write_res = write(writer, &to_write);
    // Successful write
    assert!(write_res.is_ok());
    let read_res = readv(reader, &mut iovecs[..]);
    assert!(read_res.is_ok());
    let read = read_res.ok().unwrap();
    // Check whether we've read all data
    assert_eq!(to_write.len(), read);
    // Cccumulate data from iovecs
    let mut read_buf = Vec::with_capacity(to_write.len());
    for iovec in &iovecs {
        read_buf.extend(iovec.as_slice().iter().cloned());
    }
    // Check whether iovecs contain all written data
    assert_eq!(read_buf.len(), to_write.len());
    // Check equality of written and read data
    assert_eq!(&read_buf, &to_write);
    let close_res = close(reader);
    assert!(close_res.is_ok());
    let close_res = close(writer);
    assert!(close_res.is_ok());
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_pwrite() {
    use std::io::Read;

    let mut file = tempfile().unwrap();
    let buf = [1u8;8];
    assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8));
    let mut file_content = Vec::new();
    file.read_to_end(&mut file_content).unwrap();
    let mut expected = vec![0u8;8];
    expected.extend(vec![1;8]);
    assert_eq!(file_content, expected);
}

#[test]
fn test_pread() {
    use std::io::Write;

    let tempdir = tempdir().unwrap();

    let path = tempdir.path().join("pread_test_file");
    let mut file = OpenOptions::new().write(true).read(true).create(true)
                                    .truncate(true).open(path).unwrap();
    let file_content: Vec<u8> = (0..64).collect();
    file.write_all(&file_content).unwrap();

    let mut buf = [0u8;16];
    assert_eq!(Ok(16), pread(file.as_raw_fd(), &mut buf, 16));
    let expected: Vec<_> = (16..32).collect();
    assert_eq!(&buf[..], &expected[..]);
}

#[test]
#[cfg(target_os = "linux")]
fn test_pwritev() {
    use std::io::Read;

    let to_write: Vec<u8> = (0..128).collect();
    let expected: Vec<u8> = [vec![0;100], to_write.clone()].concat();

    let iovecs = [
        IoVec::from_slice(&to_write[0..17]),
        IoVec::from_slice(&to_write[17..64]),
        IoVec::from_slice(&to_write[64..128]),
    ];

    let tempdir = tempdir().unwrap();

    // pwritev them into a temporary file
    let path = tempdir.path().join("pwritev_test_file");
    let mut file = OpenOptions::new().write(true).read(true).create(true)
                                    .truncate(true).open(path).unwrap();

    let written = pwritev(file.as_raw_fd(), &iovecs, 100).ok().unwrap();
    assert_eq!(written, to_write.len());

    // Read the data back and make sure it matches
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(contents, expected);
}

#[test]
#[cfg(target_os = "linux")]
fn test_preadv() {
    use std::io::Write;

    let to_write: Vec<u8> = (0..200).collect();
    let expected: Vec<u8> = (100..200).collect();

    let tempdir = tempdir().unwrap();

    let path = tempdir.path().join("preadv_test_file");

    let mut file = OpenOptions::new().read(true).write(true).create(true)
                                    .truncate(true).open(path).unwrap();
    file.write_all(&to_write).unwrap();

    let mut buffers: Vec<Vec<u8>> = vec![
        vec![0; 24],
        vec![0; 1],
        vec![0; 75],
    ];

    {
        // Borrow the buffers into IoVecs and preadv into them
        let iovecs: Vec<_> = buffers.iter_mut().map(
            |buf| IoVec::from_mut_slice(&mut buf[..])).collect();
        assert_eq!(Ok(100), preadv(file.as_raw_fd(), &iovecs, 100));
    }

    let all = buffers.concat();
    assert_eq!(all, expected);
}

#[test]
#[cfg(target_os = "linux")]
// qemu-user doesn't implement process_vm_readv/writev on most arches
#[cfg_attr(qemu, ignore)]
fn test_process_vm_readv() {
    use nix::unistd::ForkResult::*;
    use nix::sys::signal::*;
    use nix::sys::wait::*;
    use crate::*;

    require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
    let _m = crate::FORK_MTX.lock();

    // Pre-allocate memory in the child, since allocation isn't safe
    // post-fork (~= async-signal-safe)
    let mut vector = vec![1u8, 2, 3, 4, 5];

    let (r, w) = pipe().unwrap();
    match unsafe{fork()}.expect("Error: Fork Failed") {
        Parent { child } => {
            close(w).unwrap();
            // wait for child
            read(r, &mut [0u8]).unwrap();
            close(r).unwrap();

            let ptr = vector.as_ptr() as usize;
            let remote_iov = RemoteIoVec { base: ptr, len: 5 };
            let mut buf = vec![0u8; 5];

            let ret = process_vm_readv(child,
                                       &[IoVec::from_mut_slice(&mut buf)],
                                       &[remote_iov]);

            kill(child, SIGTERM).unwrap();
            waitpid(child, None).unwrap();

            assert_eq!(Ok(5), ret);
            assert_eq!(20u8, buf.iter().sum());
        },
        Child => {
            let _ = close(r);
            for i in &mut vector {
                *i += 1;
            }
            let _ = write(w, b"\0");
            let _ = close(w);
            loop { let _ = pause(); }
        },
    }
}
<commit_msg>Test preadv and pwritev on more operating systems<commit_after>use nix::sys::uio::*;
use nix::unistd::*;
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
use std::{cmp, iter};
use std::fs::{OpenOptions};
use std::os::unix::io::AsRawFd;

#[cfg(not(target_os = "redox"))]
use tempfile::tempfile;
use tempfile::tempdir;

#[test]
fn test_writev() {
    let mut to_write = Vec::with_capacity(16 * 128);
    for _ in 0..16 {
        let s: String = thread_rng()
            .sample_iter(&Alphanumeric)
            .map(char::from)
            .take(128)
            .collect();
        let b = s.as_bytes();
        to_write.extend(b.iter().cloned());
    }
    // Allocate and fill iovecs
    let mut iovecs = Vec::new();
    let mut consumed = 0;
    while consumed < to_write.len() {
        let left = to_write.len() - consumed;
        let slice_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) };
        let b = &to_write[consumed..consumed+slice_len];
        iovecs.push(IoVec::from_slice(b));
        consumed += slice_len;
    }
    let pipe_res = pipe();
    assert!(pipe_res.is_ok());
    let (reader, writer) = pipe_res.ok().unwrap();
    // FileDesc will close its filedesc (reader).
    let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
    // Blocking io, should write all data.
    let write_res = writev(writer, &iovecs);
    // Successful write
    assert!(write_res.is_ok());
    let written = write_res.ok().unwrap();
    // Check whether we written all data
    assert_eq!(to_write.len(), written);
    let read_res = read(reader, &mut read_buf[..]);
    // Successful read
    assert!(read_res.is_ok());
    let read = read_res.ok().unwrap() as usize;
    // Check we have read as much as we written
    assert_eq!(read, written);
    // Check equality of written and read data
    assert_eq!(&to_write, &read_buf);
    let close_res = close(writer);
    assert!(close_res.is_ok());
    let close_res = close(reader);
    assert!(close_res.is_ok());
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_readv() {
    let s:String = thread_rng()
        .sample_iter(&Alphanumeric)
        .map(char::from)
        .take(128)
        .collect();
    let to_write = s.as_bytes().to_vec();
    let mut storage = Vec::new();
    let mut allocated = 0;
    while allocated < to_write.len() {
        let left = to_write.len() - allocated;
        let vec_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) };
        let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
        storage.push(v);
        allocated += vec_len;
    }
    let mut iovecs = Vec::with_capacity(storage.len());
    for v in &mut storage {
        iovecs.push(IoVec::from_mut_slice(&mut v[..]));
    }
    let pipe_res = pipe();
    assert!(pipe_res.is_ok());
    let (reader, writer) = pipe_res.ok().unwrap();
    // Blocking io, should write all data.
    let write_res = write(writer, &to_write);
    // Successful write
    assert!(write_res.is_ok());
    let read_res = readv(reader, &mut iovecs[..]);
    assert!(read_res.is_ok());
    let read = read_res.ok().unwrap();
    // Check whether we've read all data
    assert_eq!(to_write.len(), read);
    // Cccumulate data from iovecs
    let mut read_buf = Vec::with_capacity(to_write.len());
    for iovec in &iovecs {
        read_buf.extend(iovec.as_slice().iter().cloned());
    }
    // Check whether iovecs contain all written data
    assert_eq!(read_buf.len(), to_write.len());
    // Check equality of written and read data
    assert_eq!(&read_buf, &to_write);
    let close_res = close(reader);
    assert!(close_res.is_ok());
    let close_res = close(writer);
    assert!(close_res.is_ok());
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_pwrite() {
    use std::io::Read;

    let mut file = tempfile().unwrap();
    let buf = [1u8;8];
    assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8));
    let mut file_content = Vec::new();
    file.read_to_end(&mut file_content).unwrap();
    let mut expected = vec![0u8;8];
    expected.extend(vec![1;8]);
    assert_eq!(file_content, expected);
}

#[test]
fn test_pread() {
    use std::io::Write;

    let tempdir = tempdir().unwrap();

    let path = tempdir.path().join("pread_test_file");
    let mut file = OpenOptions::new().write(true).read(true).create(true)
                                    .truncate(true).open(path).unwrap();
    let file_content: Vec<u8> = (0..64).collect();
    file.write_all(&file_content).unwrap();

    let mut buf = [0u8;16];
    assert_eq!(Ok(16), pread(file.as_raw_fd(), &mut buf, 16));
    let expected: Vec<_> = (16..32).collect();
    assert_eq!(&buf[..], &expected[..]);
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_pwritev() {
    use std::io::Read;

    let to_write: Vec<u8> = (0..128).collect();
    let expected: Vec<u8> = [vec![0;100], to_write.clone()].concat();

    let iovecs = [
        IoVec::from_slice(&to_write[0..17]),
        IoVec::from_slice(&to_write[17..64]),
        IoVec::from_slice(&to_write[64..128]),
    ];

    let tempdir = tempdir().unwrap();

    // pwritev them into a temporary file
    let path = tempdir.path().join("pwritev_test_file");
    let mut file = OpenOptions::new().write(true).read(true).create(true)
                                    .truncate(true).open(path).unwrap();

    let written = pwritev(file.as_raw_fd(), &iovecs, 100).ok().unwrap();
    assert_eq!(written, to_write.len());

    // Read the data back and make sure it matches
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(contents, expected);
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_preadv() {
    use std::io::Write;

    let to_write: Vec<u8> = (0..200).collect();
    let expected: Vec<u8> = (100..200).collect();

    let tempdir = tempdir().unwrap();

    let path = tempdir.path().join("preadv_test_file");

    let mut file = OpenOptions::new().read(true).write(true).create(true)
                                    .truncate(true).open(path).unwrap();
    file.write_all(&to_write).unwrap();

    let mut buffers: Vec<Vec<u8>> = vec![
        vec![0; 24],
        vec![0; 1],
        vec![0; 75],
    ];

    {
        // Borrow the buffers into IoVecs and preadv into them
        let iovecs: Vec<_> = buffers.iter_mut().map(
            |buf| IoVec::from_mut_slice(&mut buf[..])).collect();
        assert_eq!(Ok(100), preadv(file.as_raw_fd(), &iovecs, 100));
    }

    let all = buffers.concat();
    assert_eq!(all, expected);
}

#[test]
#[cfg(target_os = "linux")]
// qemu-user doesn't implement process_vm_readv/writev on most arches
#[cfg_attr(qemu, ignore)]
fn test_process_vm_readv() {
    use nix::unistd::ForkResult::*;
    use nix::sys::signal::*;
    use nix::sys::wait::*;
    use crate::*;

    require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
    let _m = crate::FORK_MTX.lock();

    // Pre-allocate memory in the child, since allocation isn't safe
    // post-fork (~= async-signal-safe)
    let mut vector = vec![1u8, 2, 3, 4, 5];

    let (r, w) = pipe().unwrap();
    match unsafe{fork()}.expect("Error: Fork Failed") {
        Parent { child } => {
            close(w).unwrap();
            // wait for child
            read(r, &mut [0u8]).unwrap();
            close(r).unwrap();

            let ptr = vector.as_ptr() as usize;
            let remote_iov = RemoteIoVec { base: ptr, len: 5 };
            let mut buf = vec![0u8; 5];

            let ret = process_vm_readv(child,
                                       &[IoVec::from_mut_slice(&mut buf)],
                                       &[remote_iov]);

            kill(child, SIGTERM).unwrap();
            waitpid(child, None).unwrap();

            assert_eq!(Ok(5), ret);
            assert_eq!(20u8, buf.iter().sum());
        },
        Child => {
            let _ = close(r);
            for i in &mut vector {
                *i += 1;
            }
            let _ = write(w, b"\0");
            let _ = close(w);
            loop { let _ = pause(); }
        },
    }
}
<|endoftext|>


<commit_before>use hir::Semantics;
use ide_db::{
    base_db::{FileId, FilePosition, FileRange, SourceDatabase},
    RootDatabase, SymbolKind,
};
use syntax::TextRange;

use crate::{
    file_structure::file_structure,
    fn_references::find_all_methods,
    goto_implementation::goto_implementation,
    references::find_all_refs,
    runnables::{runnables, Runnable},
    NavigationTarget, RunnableKind,
};

// Feature: Annotations
//
// Provides user with annotations above items for looking up references or impl blocks
// and running/debugging binaries.
#[derive(Debug)]
pub struct Annotation {
    pub range: TextRange,
    pub kind: AnnotationKind,
}

#[derive(Debug)]
pub enum AnnotationKind {
    Runnable { debug: bool, runnable: Runnable },
    HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> },
    HasReferences { position: FilePosition, data: Option<Vec<FileRange>> },
}

pub struct AnnotationConfig {
    pub binary_target: bool,
    pub annotate_runnables: bool,
    pub annotate_impls: bool,
    pub annotate_references: bool,
    pub annotate_method_references: bool,
    pub run: bool,
    pub debug: bool,
}

pub(crate) fn annotations(
    db: &RootDatabase,
    file_id: FileId,
    config: AnnotationConfig,
) -> Vec<Annotation> {
    let mut annotations = Vec::default();

    if config.annotate_runnables {
        for runnable in runnables(db, file_id) {
            if !matches!(runnable.kind, RunnableKind::Bin) || !config.binary_target {
                continue;
            }

            let action = runnable.action();
            let range = runnable.nav.full_range;

            if config.run {
                annotations.push(Annotation {
                    range,
                    // FIXME: This one allocates without reason if run is enabled, but debug is disabled
                    kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() },
                });
            }

            if action.debugee && config.debug {
                annotations.push(Annotation {
                    range,
                    kind: AnnotationKind::Runnable { debug: true, runnable },
                });
            }
        }
    }

    file_structure(&db.parse(file_id).tree())
        .into_iter()
        .filter(|node| {
            matches!(
                node.kind,
                SymbolKind::Trait
                    | SymbolKind::Struct
                    | SymbolKind::Enum
                    | SymbolKind::Union
                    | SymbolKind::Const
            )
        })
        .for_each(|node| {
            if config.annotate_impls && node.kind != SymbolKind::Const {
                annotations.push(Annotation {
                    range: node.node_range,
                    kind: AnnotationKind::HasImpls {
                        position: FilePosition { file_id, offset: node.navigation_range.start() },
                        data: None,
                    },
                });
            }

            if config.annotate_references {
                annotations.push(Annotation {
                    range: node.node_range,
                    kind: AnnotationKind::HasReferences {
                        position: FilePosition { file_id, offset: node.navigation_range.start() },
                        data: None,
                    },
                });
            }
        });

    if config.annotate_method_references {
        annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
            range: method.range,
            kind: AnnotationKind::HasReferences {
                position: FilePosition { file_id, offset: method.range.start() },
                data: None,
            },
        }));
    }

    annotations
}

pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
    match annotation.kind {
        AnnotationKind::HasImpls { position, ref mut data } => {
            *data = goto_implementation(db, position).map(|range| range.info);
        }
        AnnotationKind::HasReferences { position, ref mut data } => {
            *data = find_all_refs(&Semantics::new(db), position, None).map(|result| {
                result
                    .references
                    .into_iter()
                    .map(|(file_id, access)| {
                        access.into_iter().map(move |(range, _)| FileRange { file_id, range })
                    })
                    .flatten()
                    .collect()
            });
        }
        _ => {}
    };

    annotation
}

#[cfg(test)]
mod tests {
    use ide_db::base_db::{FileId, FileRange};
    use syntax::{TextRange, TextSize};

    use crate::{fixture, Annotation, AnnotationConfig, AnnotationKind, RunnableKind};

    fn get_annotations(
        ra_fixture: &str,
        annotation_config: AnnotationConfig,
    ) -> (FileId, Vec<Annotation>) {
        let (analysis, file_id) = fixture::file(ra_fixture);

        let annotations: Vec<Annotation> = analysis
            .annotations(file_id, annotation_config)
            .unwrap()
            .into_iter()
            .map(move |annotation| analysis.resolve_annotation(annotation).unwrap())
            .collect();

        if annotations.len() == 0 {
            panic!("unresolved annotations")
        }

        (file_id, annotations)
    }

    macro_rules! check_annotation {
        ( $ra_fixture:expr, $config:expr, $item_positions:expr, $pattern:pat, $checker:expr ) => {
            let (file_id, annotations) = get_annotations($ra_fixture, $config);

            annotations.into_iter().for_each(|annotation| {
                assert!($item_positions.contains(&annotation.range));

                match annotation.kind {
                    $pattern => $checker(file_id),
                    _ => panic!("Unexpected annotation kind"),
                }
            });
        };
    }

    #[test]
    fn const_annotations() {
        check_annotation!(
            r#"
const DEMO: i32 = 123;

fn main() {
    let hello = DEMO;
}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: false,
                annotate_references: true,
                annotate_method_references: false,
                run: false,
                debug: false,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(22))],
            AnnotationKind::HasReferences { data: Some(ranges), .. },
            |file_id| assert_eq!(
                *ranges.first().unwrap(),
                FileRange {
                    file_id,
                    range: TextRange::new(TextSize::from(52), TextSize::from(56))
                }
            )
        );
    }

    #[test]
    fn unused_const_annotations() {
        check_annotation!(
            r#"
const DEMO: i32 = 123;

fn main() {}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: false,
                annotate_references: true,
                annotate_method_references: false,
                run: false,
                debug: false,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(22))],
            AnnotationKind::HasReferences { data: Some(ranges), .. },
            |_| assert_eq!(ranges.len(), 0)
        );
    }

    #[test]
    fn struct_references_annotations() {
        check_annotation!(
            r#"
struct Test;

fn main() {
    let test = Test;
}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: false,
                annotate_references: true,
                annotate_method_references: false,
                run: false,
                debug: false,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(12))],
            AnnotationKind::HasReferences { data: Some(ranges), .. },
            |file_id| assert_eq!(
                *ranges.first().unwrap(),
                FileRange {
                    file_id,
                    range: TextRange::new(TextSize::from(41), TextSize::from(45))
                }
            )
        );
    }

    #[test]
    fn struct_and_trait_impls_annotations() {
        check_annotation!(
            r#"
struct Test;

trait MyCoolTrait {}

impl MyCoolTrait for Test {}

fn main() {
    let test = Test;
}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: true,
                annotate_references: false,
                annotate_method_references: false,
                run: false,
                debug: false,
            },
            &[
                TextRange::new(TextSize::from(0), TextSize::from(12)),
                TextRange::new(TextSize::from(14), TextSize::from(34))
            ],
            AnnotationKind::HasImpls { data: Some(ranges), .. },
            |_| assert_eq!(
                ranges.first().unwrap().full_range,
                TextRange::new(TextSize::from(36), TextSize::from(64))
            )
        );
    }

    #[test]
    fn run_annotation() {
        check_annotation!(
            r#"
fn main() {}
            "#,
            AnnotationConfig {
                binary_target: true,
                annotate_runnables: true,
                annotate_impls: false,
                annotate_references: false,
                annotate_method_references: false,
                run: true,
                debug: false,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(12))],
            AnnotationKind::Runnable { debug: false, runnable },
            |_| {
                assert!(matches!(runnable.kind, RunnableKind::Bin));
                assert!(runnable.action().run_title.contains("Run"));
            }
        );
    }

    #[test]
    fn debug_annotation() {
        check_annotation!(
            r#"
fn main() {}
            "#,
            AnnotationConfig {
                binary_target: true,
                annotate_runnables: true,
                annotate_impls: false,
                annotate_references: false,
                annotate_method_references: false,
                run: false,
                debug: true,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(12))],
            AnnotationKind::Runnable { debug: true, runnable },
            |_| {
                assert!(matches!(runnable.kind, RunnableKind::Bin));
                assert!(runnable.action().debugee);
            }
        );
    }

    #[test]
    fn method_annotations() {
        // We actually want to skip `fn main` annotation, as it has no references in it
        // but just ignoring empty reference slices would lead to false-positive if something
        // goes wrong in annotation resolving mechanism. By tracking if we iterated before finding
        // an empty slice we can track if everything is settled.
        let mut iterated_once = false;

        check_annotation!(
            r#"
struct Test;

impl Test {
    fn self_by_ref(&self) {}
}

fn main() {
    Test.self_by_ref();
}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: false,
                annotate_references: false,
                annotate_method_references: true,
                run: false,
                debug: false,
            },
            &[
                TextRange::new(TextSize::from(33), TextSize::from(44)),
                TextRange::new(TextSize::from(61), TextSize::from(65))
            ],
            AnnotationKind::HasReferences { data: Some(ranges), .. },
            |file_id| {
                match ranges.as_slice() {
                    [first, ..] => {
                        assert_eq!(
                            *first,
                            FileRange {
                                file_id,
                                range: TextRange::new(TextSize::from(79), TextSize::from(90))
                            }
                        );

                        iterated_once = true;
                    }
                    [] if iterated_once => {}
                    [] => panic!("One reference was expected but not found"),
                }
            }
        );
    }
}
<commit_msg>Improve runnable annotations order, fix incorrect ignore detection<commit_after>use hir::Semantics;
use ide_db::{
    base_db::{FileId, FilePosition, FileRange, SourceDatabase},
    RootDatabase, SymbolKind,
};
use syntax::TextRange;

use crate::{
    file_structure::file_structure,
    fn_references::find_all_methods,
    goto_implementation::goto_implementation,
    references::find_all_refs,
    runnables::{runnables, Runnable},
    NavigationTarget, RunnableKind,
};

// Feature: Annotations
//
// Provides user with annotations above items for looking up references or impl blocks
// and running/debugging binaries.
#[derive(Debug)]
pub struct Annotation {
    pub range: TextRange,
    pub kind: AnnotationKind,
}

#[derive(Debug)]
pub enum AnnotationKind {
    Runnable { debug: bool, runnable: Runnable },
    HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> },
    HasReferences { position: FilePosition, data: Option<Vec<FileRange>> },
}

pub struct AnnotationConfig {
    pub binary_target: bool,
    pub annotate_runnables: bool,
    pub annotate_impls: bool,
    pub annotate_references: bool,
    pub annotate_method_references: bool,
    pub run: bool,
    pub debug: bool,
}

pub(crate) fn annotations(
    db: &RootDatabase,
    file_id: FileId,
    config: AnnotationConfig,
) -> Vec<Annotation> {
    let mut annotations = Vec::default();

    if config.annotate_runnables {
        for runnable in runnables(db, file_id) {
            if should_skip_runnable(&runnable.kind, config.binary_target) {
                continue;
            }

            let action = runnable.action();
            let range = runnable.nav.full_range;

            if action.debugee && config.debug {
                annotations.push(Annotation {
                    range,

                    // FIXME: This one allocates without reason if run is enabled, but debug is disabled
                    kind: AnnotationKind::Runnable { debug: true, runnable: runnable.clone() },
                });
            }

            if config.run {
                annotations.push(Annotation {
                    range,
                    kind: AnnotationKind::Runnable { debug: false, runnable },
                });
            }
        }
    }

    file_structure(&db.parse(file_id).tree())
        .into_iter()
        .filter(|node| {
            matches!(
                node.kind,
                SymbolKind::Trait
                    | SymbolKind::Struct
                    | SymbolKind::Enum
                    | SymbolKind::Union
                    | SymbolKind::Const
            )
        })
        .for_each(|node| {
            if config.annotate_impls && node.kind != SymbolKind::Const {
                annotations.push(Annotation {
                    range: node.node_range,
                    kind: AnnotationKind::HasImpls {
                        position: FilePosition { file_id, offset: node.navigation_range.start() },
                        data: None,
                    },
                });
            }

            if config.annotate_references {
                annotations.push(Annotation {
                    range: node.node_range,
                    kind: AnnotationKind::HasReferences {
                        position: FilePosition { file_id, offset: node.navigation_range.start() },
                        data: None,
                    },
                });
            }
        });

    if config.annotate_method_references {
        annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
            range: method.range,
            kind: AnnotationKind::HasReferences {
                position: FilePosition { file_id, offset: method.range.start() },
                data: None,
            },
        }));
    }

    annotations
}

pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
    match annotation.kind {
        AnnotationKind::HasImpls { position, ref mut data } => {
            *data = goto_implementation(db, position).map(|range| range.info);
        }
        AnnotationKind::HasReferences { position, ref mut data } => {
            *data = find_all_refs(&Semantics::new(db), position, None).map(|result| {
                result
                    .references
                    .into_iter()
                    .map(|(file_id, access)| {
                        access.into_iter().map(move |(range, _)| FileRange { file_id, range })
                    })
                    .flatten()
                    .collect()
            });
        }
        _ => {}
    };

    annotation
}

fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool {
    match kind {
        RunnableKind::Bin => !binary_target,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use ide_db::base_db::{FileId, FileRange};
    use syntax::{TextRange, TextSize};

    use crate::{fixture, Annotation, AnnotationConfig, AnnotationKind, RunnableKind};

    fn get_annotations(
        ra_fixture: &str,
        annotation_config: AnnotationConfig,
    ) -> (FileId, Vec<Annotation>) {
        let (analysis, file_id) = fixture::file(ra_fixture);

        let annotations: Vec<Annotation> = analysis
            .annotations(file_id, annotation_config)
            .unwrap()
            .into_iter()
            .map(move |annotation| analysis.resolve_annotation(annotation).unwrap())
            .collect();

        if annotations.len() == 0 {
            panic!("unresolved annotations")
        }

        (file_id, annotations)
    }

    macro_rules! check_annotation {
        ( $ra_fixture:expr, $config:expr, $item_positions:expr, $pattern:pat, $checker:expr ) => {
            let (file_id, annotations) = get_annotations($ra_fixture, $config);

            annotations.into_iter().for_each(|annotation| {
                assert!($item_positions.contains(&annotation.range));

                match annotation.kind {
                    $pattern => $checker(file_id),
                    _ => panic!("Unexpected annotation kind"),
                }
            });
        };
    }

    #[test]
    fn const_annotations() {
        check_annotation!(
            r#"
const DEMO: i32 = 123;

fn main() {
    let hello = DEMO;
}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: false,
                annotate_references: true,
                annotate_method_references: false,
                run: false,
                debug: false,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(22))],
            AnnotationKind::HasReferences { data: Some(ranges), .. },
            |file_id| assert_eq!(
                *ranges.first().unwrap(),
                FileRange {
                    file_id,
                    range: TextRange::new(TextSize::from(52), TextSize::from(56))
                }
            )
        );
    }

    #[test]
    fn unused_const_annotations() {
        check_annotation!(
            r#"
const DEMO: i32 = 123;

fn main() {}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: false,
                annotate_references: true,
                annotate_method_references: false,
                run: false,
                debug: false,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(22))],
            AnnotationKind::HasReferences { data: Some(ranges), .. },
            |_| assert_eq!(ranges.len(), 0)
        );
    }

    #[test]
    fn struct_references_annotations() {
        check_annotation!(
            r#"
struct Test;

fn main() {
    let test = Test;
}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: false,
                annotate_references: true,
                annotate_method_references: false,
                run: false,
                debug: false,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(12))],
            AnnotationKind::HasReferences { data: Some(ranges), .. },
            |file_id| assert_eq!(
                *ranges.first().unwrap(),
                FileRange {
                    file_id,
                    range: TextRange::new(TextSize::from(41), TextSize::from(45))
                }
            )
        );
    }

    #[test]
    fn struct_and_trait_impls_annotations() {
        check_annotation!(
            r#"
struct Test;

trait MyCoolTrait {}

impl MyCoolTrait for Test {}

fn main() {
    let test = Test;
}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: true,
                annotate_references: false,
                annotate_method_references: false,
                run: false,
                debug: false,
            },
            &[
                TextRange::new(TextSize::from(0), TextSize::from(12)),
                TextRange::new(TextSize::from(14), TextSize::from(34))
            ],
            AnnotationKind::HasImpls { data: Some(ranges), .. },
            |_| assert_eq!(
                ranges.first().unwrap().full_range,
                TextRange::new(TextSize::from(36), TextSize::from(64))
            )
        );
    }

    #[test]
    fn run_annotation() {
        check_annotation!(
            r#"
fn main() {}
            "#,
            AnnotationConfig {
                binary_target: true,
                annotate_runnables: true,
                annotate_impls: false,
                annotate_references: false,
                annotate_method_references: false,
                run: true,
                debug: false,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(12))],
            AnnotationKind::Runnable { debug: false, runnable },
            |_| {
                assert!(matches!(runnable.kind, RunnableKind::Bin));
                assert!(runnable.action().run_title.contains("Run"));
            }
        );
    }

    #[test]
    fn debug_annotation() {
        check_annotation!(
            r#"
fn main() {}
            "#,
            AnnotationConfig {
                binary_target: true,
                annotate_runnables: true,
                annotate_impls: false,
                annotate_references: false,
                annotate_method_references: false,
                run: false,
                debug: true,
            },
            &[TextRange::new(TextSize::from(0), TextSize::from(12))],
            AnnotationKind::Runnable { debug: true, runnable },
            |_| {
                assert!(matches!(runnable.kind, RunnableKind::Bin));
                assert!(runnable.action().debugee);
            }
        );
    }

    #[test]
    fn method_annotations() {
        // We actually want to skip `fn main` annotation, as it has no references in it
        // but just ignoring empty reference slices would lead to false-positive if something
        // goes wrong in annotation resolving mechanism. By tracking if we iterated before finding
        // an empty slice we can track if everything is settled.
        let mut iterated_once = false;

        check_annotation!(
            r#"
struct Test;

impl Test {
    fn self_by_ref(&self) {}
}

fn main() {
    Test.self_by_ref();
}
            "#,
            AnnotationConfig {
                binary_target: false,
                annotate_runnables: false,
                annotate_impls: false,
                annotate_references: false,
                annotate_method_references: true,
                run: false,
                debug: false,
            },
            &[
                TextRange::new(TextSize::from(33), TextSize::from(44)),
                TextRange::new(TextSize::from(61), TextSize::from(65))
            ],
            AnnotationKind::HasReferences { data: Some(ranges), .. },
            |file_id| {
                match ranges.as_slice() {
                    [first, ..] => {
                        assert_eq!(
                            *first,
                            FileRange {
                                file_id,
                                range: TextRange::new(TextSize::from(79), TextSize::from(90))
                            }
                        );

                        iterated_once = true;
                    }
                    [] if iterated_once => {}
                    [] => panic!("One reference was expected but not found"),
                }
            }
        );
    }
}
<|endoftext|>


<commit_before>//! Arnoldi iteration

use super::*;
use crate::norm::Norm;
use num_traits::One;
use std::iter::*;

/// Execute Arnoldi iteration as Rust iterator
///
/// - [Arnoldi iteration - Wikipedia](https://en.wikipedia.org/wiki/Arnoldi_iteration)
///
pub struct Arnoldi<A, S, F, Ortho>
where
    A: Scalar,
    S: DataMut<Elem = A>,
    F: Fn(&mut ArrayBase<S, Ix1>),
    Ortho: Orthogonalizer<Elem = A>,
{
    a: F,
    /// Next vector (normalized `|v|=1`)
    v: ArrayBase<S, Ix1>,
    /// Orthogonalizer
    ortho: Ortho,
    /// Coefficients to be composed into H-matrix
    h: Vec<Array1<A>>,
}

impl<A, S, F, Ortho> Arnoldi<A, S, F, Ortho>
where
    A: Scalar + Lapack,
    S: DataMut<Elem = A>,
    F: Fn(&mut ArrayBase<S, Ix1>),
    Ortho: Orthogonalizer<Elem = A>,
{
    /// Create an Arnoldi iterator from any linear operator `a`
    pub fn new(a: F, mut v: ArrayBase<S, Ix1>, mut ortho: Ortho) -> Self {
        assert_eq!(ortho.len(), 0);
        assert!(ortho.tolerance() < One::one());
        // normalize before append because |v| may be smaller than ortho.tolerance()
        let norm = v.norm_l2();
        azip!(mut v(&mut v) in { *v = v.div_real(norm) });
        ortho.append(v.view());
        Arnoldi {
            a,
            v,
            ortho,
            h: Vec::new(),
        }
    }

    /// Dimension of Krylov subspace
    pub fn dim(&self) -> usize {
        self.ortho.len()
    }

    /// Iterate until convergent
    pub fn complete(mut self) -> (Q<A>, H<A>) {
        for _ in &mut self {} // execute iteration until convergent
        let q = self.ortho.get_q();
        let n = self.h.len();
        let mut h = Array2::zeros((n, n).f());
        for (i, hc) in self.h.iter().enumerate() {
            let m = std::cmp::min(n, i + 2);
            for j in 0..m {
                h[(j, i)] = hc[j];
            }
        }
        (q, h)
    }
}

impl<A, S, F, Ortho> Iterator for Arnoldi<A, S, F, Ortho>
where
    A: Scalar + Lapack,
    S: DataMut<Elem = A>,
    F: Fn(&mut ArrayBase<S, Ix1>),
    Ortho: Orthogonalizer<Elem = A>,
{
    type Item = Array1<A>;

    fn next(&mut self) -> Option<Self::Item> {
        (self.a)(&mut self.v);
        let result = self.ortho.div_append(&mut self.v);
        let norm = self.v.norm_l2();
        azip!(mut v(&mut self.v) in { *v = v.div_real(norm) });
        match result {
            AppendResult::Added(coef) => {
                self.h.push(coef.clone());
                Some(coef)
            }
            AppendResult::Dependent(coef) => {
                self.h.push(coef);
                None
            }
        }
    }
}

/// Interpret a matrix as a linear operator
pub fn mul_mat<A, S1, S2>(a: ArrayBase<S1, Ix2>) -> impl Fn(&mut ArrayBase<S2, Ix1>)
where
    A: Scalar,
    S1: Data<Elem = A>,
    S2: DataMut<Elem = A>,
{
    let (n, m) = a.dim();
    assert_eq!(n, m, "Input matrix must be square");
    move |x| {
        assert_eq!(m, x.len(), "Input matrix and vector sizes mismatch");
        let ax = a.dot(x);
        azip!(mut x(x), ax in { *x = ax });
    }
}

/// Utility to execute Arnoldi iteration with Householder reflection
pub fn arnoldi_householder<A, S1, S2>(a: ArrayBase<S1, Ix2>, v: ArrayBase<S2, Ix1>, tol: A::Real) -> (Q<A>, H<A>)
where
    A: Scalar + Lapack,
    S1: Data<Elem = A>,
    S2: DataMut<Elem = A>,
{
    let householder = Householder::new(v.len(), tol);
    Arnoldi::new(mul_mat(a), v, householder).complete()
}

/// Utility to execute Arnoldi iteration with modified Gram-Schmit orthogonalizer
pub fn arnoldi_mgs<A, S1, S2>(a: ArrayBase<S1, Ix2>, v: ArrayBase<S2, Ix1>, tol: A::Real) -> (Q<A>, H<A>)
where
    A: Scalar + Lapack,
    S1: Data<Elem = A>,
    S2: DataMut<Elem = A>,
{
    let mgs = MGS::new(v.len(), tol);
    Arnoldi::new(mul_mat(a), v, mgs).complete()
}
<commit_msg>Use LinearOperator on Arnoldi<commit_after>//! Arnoldi iteration

use super::*;
use crate::{norm::Norm, operator::LinearOperator};
use num_traits::One;
use std::iter::*;

/// Execute Arnoldi iteration as Rust iterator
///
/// - [Arnoldi iteration - Wikipedia](https://en.wikipedia.org/wiki/Arnoldi_iteration)
///
pub struct Arnoldi<A, S, F, Ortho>
where
    A: Scalar,
    S: DataMut<Elem = A>,
    F: LinearOperator<Elem = A>,
    Ortho: Orthogonalizer<Elem = A>,
{
    a: F,
    /// Next vector (normalized `|v|=1`)
    v: ArrayBase<S, Ix1>,
    /// Orthogonalizer
    ortho: Ortho,
    /// Coefficients to be composed into H-matrix
    h: Vec<Array1<A>>,
}

impl<A, S, F, Ortho> Arnoldi<A, S, F, Ortho>
where
    A: Scalar + Lapack,
    S: DataMut<Elem = A>,
    F: LinearOperator<Elem = A>,
    Ortho: Orthogonalizer<Elem = A>,
{
    /// Create an Arnoldi iterator from any linear operator `a`
    pub fn new(a: F, mut v: ArrayBase<S, Ix1>, mut ortho: Ortho) -> Self {
        assert_eq!(ortho.len(), 0);
        assert!(ortho.tolerance() < One::one());
        // normalize before append because |v| may be smaller than ortho.tolerance()
        let norm = v.norm_l2();
        azip!(mut v(&mut v) in { *v = v.div_real(norm) });
        ortho.append(v.view());
        Arnoldi {
            a,
            v,
            ortho,
            h: Vec::new(),
        }
    }

    /// Dimension of Krylov subspace
    pub fn dim(&self) -> usize {
        self.ortho.len()
    }

    /// Iterate until convergent
    pub fn complete(mut self) -> (Q<A>, H<A>) {
        for _ in &mut self {} // execute iteration until convergent
        let q = self.ortho.get_q();
        let n = self.h.len();
        let mut h = Array2::zeros((n, n).f());
        for (i, hc) in self.h.iter().enumerate() {
            let m = std::cmp::min(n, i + 2);
            for j in 0..m {
                h[(j, i)] = hc[j];
            }
        }
        (q, h)
    }
}

impl<A, S, F, Ortho> Iterator for Arnoldi<A, S, F, Ortho>
where
    A: Scalar + Lapack,
    S: DataMut<Elem = A>,
    F: LinearOperator<Elem = A>,
    Ortho: Orthogonalizer<Elem = A>,
{
    type Item = Array1<A>;

    fn next(&mut self) -> Option<Self::Item> {
        self.a.apply_mut(&mut self.v);
        let result = self.ortho.div_append(&mut self.v);
        let norm = self.v.norm_l2();
        azip!(mut v(&mut self.v) in { *v = v.div_real(norm) });
        match result {
            AppendResult::Added(coef) => {
                self.h.push(coef.clone());
                Some(coef)
            }
            AppendResult::Dependent(coef) => {
                self.h.push(coef);
                None
            }
        }
    }
}

/// Utility to execute Arnoldi iteration with Householder reflection
pub fn arnoldi_householder<A, S>(a: impl LinearOperator<Elem = A>, v: ArrayBase<S, Ix1>, tol: A::Real) -> (Q<A>, H<A>)
where
    A: Scalar + Lapack,
    S: DataMut<Elem = A>,
{
    let householder = Householder::new(v.len(), tol);
    Arnoldi::new(a, v, householder).complete()
}

/// Utility to execute Arnoldi iteration with modified Gram-Schmit orthogonalizer
pub fn arnoldi_mgs<A, S>(a: impl LinearOperator<Elem = A>, v: ArrayBase<S, Ix1>, tol: A::Real) -> (Q<A>, H<A>)
where
    A: Scalar + Lapack,
    S: DataMut<Elem = A>,
{
    let mgs = MGS::new(v.len(), tol);
    Arnoldi::new(a, v, mgs).complete()
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Little helpers for `nsCSSValue`.

use gecko_bindings::bindings;
use gecko_bindings::structs;
use gecko_bindings::structs::{nsCSSUnit, nsCSSValue};
use gecko_bindings::structs::{nsCSSValueList, nsCSSValue_Array};
use gecko_string_cache::Atom;
use std::marker::PhantomData;
use std::mem;
use std::ops::{Index, IndexMut};
use std::slice;
use values::computed::{Angle, Length, LengthOrPercentage, Percentage};

impl nsCSSValue {
    /// Create a CSSValue with null unit, useful to be used as a return value.
    #[inline]
    pub fn null() -> Self {
        unsafe { mem::zeroed() }
    }

    /// Returns true if this nsCSSValue is none.
    #[inline]
    pub fn is_none(&self) -> bool {
        self.mUnit == nsCSSUnit::eCSSUnit_None
    }

    /// Returns this nsCSSValue value as an integer, unchecked in release
    /// builds.
    pub fn integer_unchecked(&self) -> i32 {
        debug_assert!(
            self.mUnit == nsCSSUnit::eCSSUnit_Integer ||
                self.mUnit == nsCSSUnit::eCSSUnit_Enumerated
        );
        unsafe { *self.mValue.mInt.as_ref() }
    }

    /// Checks if it is an integer and returns it if so
    pub fn integer(&self) -> Option<i32> {
        if self.mUnit == nsCSSUnit::eCSSUnit_Integer || self.mUnit == nsCSSUnit::eCSSUnit_Enumerated
        {
            Some(unsafe { *self.mValue.mInt.as_ref() })
        } else {
            None
        }
    }

    /// Returns this nsCSSValue value as a floating point value, unchecked in
    /// release builds.
    pub fn float_unchecked(&self) -> f32 {
        debug_assert!(nsCSSUnit::eCSSUnit_Number as u32 <= self.mUnit as u32);
        unsafe { *self.mValue.mFloat.as_ref() }
    }

    /// Returns this nsCSSValue as a nsCSSValue::Array, unchecked in release
    /// builds.
    pub unsafe fn array_unchecked(&self) -> &nsCSSValue_Array {
        debug_assert!(
            nsCSSUnit::eCSSUnit_Array as u32 <= self.mUnit as u32 &&
                self.mUnit as u32 <= nsCSSUnit::eCSSUnit_Calc_Divided as u32
        );
        let array = *self.mValue.mArray.as_ref();
        debug_assert!(!array.is_null());
        &*array
    }

    /// Sets LengthOrPercentage value to this nsCSSValue.
    pub unsafe fn set_lop(&mut self, lop: LengthOrPercentage) {
        match lop {
            LengthOrPercentage::Length(px) => self.set_px(px.px()),
            LengthOrPercentage::Percentage(pc) => self.set_percentage(pc.0),
            LengthOrPercentage::Calc(calc) => bindings::Gecko_CSSValue_SetCalc(self, calc.into()),
        }
    }

    /// Sets a px value to this nsCSSValue.
    pub unsafe fn set_px(&mut self, px: f32) {
        bindings::Gecko_CSSValue_SetPixelLength(self, px)
    }

    /// Sets a percentage value to this nsCSSValue.
    pub unsafe fn set_percentage(&mut self, unit_value: f32) {
        bindings::Gecko_CSSValue_SetPercentage(self, unit_value)
    }

    /// Returns LengthOrPercentage value.
    pub unsafe fn get_lop(&self) -> LengthOrPercentage {
        match self.mUnit {
            nsCSSUnit::eCSSUnit_Pixel => {
                LengthOrPercentage::Length(Length::new(bindings::Gecko_CSSValue_GetNumber(self)))
            },
            nsCSSUnit::eCSSUnit_Percent => LengthOrPercentage::Percentage(Percentage(
                bindings::Gecko_CSSValue_GetPercentage(self),
            )),
            nsCSSUnit::eCSSUnit_Calc => {
                LengthOrPercentage::Calc(bindings::Gecko_CSSValue_GetCalc(self).into())
            },
            _ => panic!("Unexpected unit"),
        }
    }

    /// Returns Length  value.
    pub unsafe fn get_length(&self) -> Length {
        match self.mUnit {
            nsCSSUnit::eCSSUnit_Pixel => Length::new(bindings::Gecko_CSSValue_GetNumber(self)),
            _ => panic!("Unexpected unit"),
        }
    }

    fn set_valueless_unit(&mut self, unit: nsCSSUnit) {
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
        debug_assert!(
            unit as u32 <= nsCSSUnit::eCSSUnit_DummyInherit as u32,
            "Not a valueless unit"
        );
        self.mUnit = unit;
    }

    /// Set to an auto value
    ///
    /// This method requires the current value to be null.
    pub fn set_auto(&mut self) {
        self.set_valueless_unit(nsCSSUnit::eCSSUnit_Auto);
    }

    /// Set to a normal value
    ///
    /// This method requires the current value to be null.
    pub fn set_normal(&mut self) {
        self.set_valueless_unit(nsCSSUnit::eCSSUnit_Normal);
    }

    fn set_string_internal(&mut self, s: &str, unit: nsCSSUnit) {
        unsafe { bindings::Gecko_CSSValue_SetString(self, s.as_ptr(), s.len() as u32, unit) }
    }

    fn set_string_from_atom_internal(&mut self, s: &Atom, unit: nsCSSUnit) {
        unsafe { bindings::Gecko_CSSValue_SetStringFromAtom(self, s.as_ptr(), unit) }
    }

    /// Set to a string value
    pub fn set_string(&mut self, s: &str) {
        self.set_string_internal(s, nsCSSUnit::eCSSUnit_String)
    }

    /// Set to a string value from the given atom
    pub fn set_string_from_atom(&mut self, s: &Atom) {
        self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_String)
    }

    /// Set to a ident value from the given atom
    pub fn set_ident_from_atom(&mut self, s: &Atom) {
        self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_Ident)
    }

    /// Set to an identifier value
    pub fn set_ident(&mut self, s: &str) {
        self.set_string_internal(s, nsCSSUnit::eCSSUnit_Ident)
    }

    /// Set to an atom identifier value
    pub fn set_atom_ident(&mut self, s: Atom) {
        unsafe { bindings::Gecko_CSSValue_SetAtomIdent(self, s.into_addrefed()) }
    }

    fn set_int_internal(&mut self, value: i32, unit: nsCSSUnit) {
        unsafe { bindings::Gecko_CSSValue_SetInt(self, value, unit) }
    }

    /// Set to an integer value
    pub fn set_integer(&mut self, value: i32) {
        self.set_int_internal(value, nsCSSUnit::eCSSUnit_Integer)
    }

    /// Set to an enumerated value
    pub fn set_enum<T: Into<i32>>(&mut self, value: T) {
        self.set_int_internal(value.into(), nsCSSUnit::eCSSUnit_Enumerated);
    }

    /// Set to a number value
    pub fn set_number(&mut self, number: f32) {
        unsafe { bindings::Gecko_CSSValue_SetFloat(self, number, nsCSSUnit::eCSSUnit_Number) }
    }

    /// Set to an array of given length
    pub fn set_array(&mut self, len: i32) -> &mut nsCSSValue_Array {
        unsafe { bindings::Gecko_CSSValue_SetArray(self, len) }
        unsafe { self.mValue.mArray.as_mut().as_mut() }.unwrap()
    }

    /// Generic set from any value that implements the ToNsCssValue trait.
    pub fn set_from<T: ToNsCssValue>(&mut self, value: T) {
        value.convert(self)
    }

    /// Returns an `Angle` value from this `nsCSSValue`.
    ///
    /// Panics if the unit is not `eCSSUnit_Degree` `eCSSUnit_Grad`, `eCSSUnit_Turn`
    /// or `eCSSUnit_Radian`.
    pub fn get_angle(&self) -> Angle {
        Angle::from_gecko_values(self.float_unchecked(), self.mUnit)
    }

    /// Sets Angle value to this nsCSSValue.
    pub fn set_angle(&mut self, angle: Angle) {
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
        let (value, unit) = angle.to_gecko_values();
        self.mUnit = unit;
        unsafe {
            *self.mValue.mFloat.as_mut() = value;
        }
    }

    /// Set to a pair value
    ///
    /// This is only supported on the main thread.
    pub fn set_pair(&mut self, x: &nsCSSValue, y: &nsCSSValue) {
        unsafe { bindings::Gecko_CSSValue_SetPair(self, x, y) }
    }

    /// Set to a list value
    ///
    /// This is only supported on the main thread.
    pub fn set_list<I>(&mut self, values: I)
    where
        I: ExactSizeIterator<Item = nsCSSValue>,
    {
        debug_assert!(values.len() > 0, "Empty list is not supported");
        unsafe {
            bindings::Gecko_CSSValue_SetList(self, values.len() as u32);
        }
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_List);
        let list: &mut structs::nsCSSValueList = &mut unsafe {
            self.mValue.mList.as_ref() // &*nsCSSValueList_heap
                .as_mut().expect("List pointer should be non-null")
        }._base;
        for (item, new_value) in list.into_iter().zip(values) {
            *item = new_value;
        }
    }

    /// Set to a pair list value
    ///
    /// This is only supported on the main thread.
    pub fn set_pair_list<I>(&mut self, mut values: I)
    where
        I: ExactSizeIterator<Item = (nsCSSValue, nsCSSValue)>,
    {
        debug_assert!(values.len() > 0, "Empty list is not supported");
        unsafe {
            bindings::Gecko_CSSValue_SetPairList(self, values.len() as u32);
        }
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_PairList);
        let mut item_ptr = &mut unsafe {
            self.mValue.mPairList.as_ref() // &*nsCSSValuePairList_heap
                .as_mut().expect("List pointer should be non-null")
        }._base as *mut structs::nsCSSValuePairList;
        while let Some(item) = unsafe { item_ptr.as_mut() } {
            let value = values.next().expect("Values shouldn't have been exhausted");
            item.mXValue = value.0;
            item.mYValue = value.1;
            item_ptr = item.mNext;
        }
        debug_assert!(values.next().is_none(), "Values should have been exhausted");
    }

    /// Set a shared list
    pub fn set_shared_list<I>(&mut self, values: I)
    where
        I: ExactSizeIterator<Item = nsCSSValue>,
    {
        debug_assert!(values.len() > 0, "Empty list is not supported");
        unsafe { bindings::Gecko_CSSValue_InitSharedList(self, values.len() as u32) };
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_SharedList);
        let list = unsafe {
            self.mValue
                .mSharedList
                .as_ref()
                .as_mut()
                .expect("List pointer should be non-null")
                .mHead
                .as_mut()
        };
        debug_assert!(list.is_some(), "New created shared list shouldn't be null");
        for (item, new_value) in list.unwrap().into_iter().zip(values) {
            *item = new_value;
        }
    }
}

impl Drop for nsCSSValue {
    fn drop(&mut self) {
        unsafe { bindings::Gecko_CSSValue_Drop(self) };
    }
}

/// Iterator of nsCSSValueList.
#[allow(non_camel_case_types)]
pub struct nsCSSValueListIterator<'a> {
    current: Option<&'a nsCSSValueList>,
}

impl<'a> Iterator for nsCSSValueListIterator<'a> {
    type Item = &'a nsCSSValue;
    fn next(&mut self) -> Option<Self::Item> {
        match self.current {
            Some(item) => {
                self.current = unsafe { item.mNext.as_ref() };
                Some(&item.mValue)
            },
            None => None,
        }
    }
}

impl<'a> IntoIterator for &'a nsCSSValueList {
    type Item = &'a nsCSSValue;
    type IntoIter = nsCSSValueListIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        nsCSSValueListIterator {
            current: Some(self),
        }
    }
}

/// Mutable Iterator of nsCSSValueList.
#[allow(non_camel_case_types)]
pub struct nsCSSValueListMutIterator<'a> {
    current: *mut nsCSSValueList,
    phantom: PhantomData<&'a mut nsCSSValue>,
}

impl<'a> Iterator for nsCSSValueListMutIterator<'a> {
    type Item = &'a mut nsCSSValue;
    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { self.current.as_mut() } {
            Some(item) => {
                self.current = item.mNext;
                Some(&mut item.mValue)
            },
            None => None,
        }
    }
}

impl<'a> IntoIterator for &'a mut nsCSSValueList {
    type Item = &'a mut nsCSSValue;
    type IntoIter = nsCSSValueListMutIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        nsCSSValueListMutIterator {
            current: self as *mut nsCSSValueList,
            phantom: PhantomData,
        }
    }
}

impl nsCSSValue_Array {
    /// Return the length of this `nsCSSValue::Array`
    #[inline]
    pub fn len(&self) -> usize {
        self.mCount
    }

    #[inline]
    fn buffer(&self) -> *const nsCSSValue {
        self.mArray.as_ptr()
    }

    /// Get the array as a slice of nsCSSValues.
    #[inline]
    pub fn as_slice(&self) -> &[nsCSSValue] {
        unsafe { slice::from_raw_parts(self.buffer(), self.len()) }
    }

    /// Get the array as a mutable slice of nsCSSValues.
    #[inline]
    pub fn as_mut_slice(&mut self) -> &mut [nsCSSValue] {
        unsafe { slice::from_raw_parts_mut(self.buffer() as *mut _, self.len()) }
    }
}

impl Index<usize> for nsCSSValue_Array {
    type Output = nsCSSValue;
    #[inline]
    fn index(&self, i: usize) -> &nsCSSValue {
        &self.as_slice()[i]
    }
}

impl IndexMut<usize> for nsCSSValue_Array {
    #[inline]
    fn index_mut(&mut self, i: usize) -> &mut nsCSSValue {
        &mut self.as_mut_slice()[i]
    }
}

/// Generic conversion to nsCSSValue
pub trait ToNsCssValue {
    /// Convert
    fn convert(self, nscssvalue: &mut nsCSSValue);
}

impl<T: ToNsCssValue> From<T> for nsCSSValue {
    fn from(value: T) -> nsCSSValue {
        let mut result = nsCSSValue::null();
        value.convert(&mut result);
        result
    }
}
<commit_msg>style: Some more nsCSSValue cleanup.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Little helpers for `nsCSSValue`.

use gecko_bindings::bindings;
use gecko_bindings::structs;
use gecko_bindings::structs::{nsCSSUnit, nsCSSValue};
use gecko_bindings::structs::{nsCSSValueList, nsCSSValue_Array};
use gecko_string_cache::Atom;
use std::marker::PhantomData;
use std::mem;
use std::ops::{Index, IndexMut};
use std::slice;
use values::computed::{Angle, Length, LengthOrPercentage, Percentage};

impl nsCSSValue {
    /// Create a CSSValue with null unit, useful to be used as a return value.
    #[inline]
    pub fn null() -> Self {
        unsafe { mem::zeroed() }
    }

    /// Returns true if this nsCSSValue is none.
    #[inline]
    pub fn is_none(&self) -> bool {
        self.mUnit == nsCSSUnit::eCSSUnit_None
    }

    /// Returns this nsCSSValue value as an integer, unchecked in release
    /// builds.
    pub fn integer_unchecked(&self) -> i32 {
        debug_assert!(
            self.mUnit == nsCSSUnit::eCSSUnit_Integer ||
                self.mUnit == nsCSSUnit::eCSSUnit_Enumerated
        );
        unsafe { *self.mValue.mInt.as_ref() }
    }

    /// Checks if it is an integer and returns it if so
    pub fn integer(&self) -> Option<i32> {
        if self.mUnit == nsCSSUnit::eCSSUnit_Integer || self.mUnit == nsCSSUnit::eCSSUnit_Enumerated
        {
            Some(unsafe { *self.mValue.mInt.as_ref() })
        } else {
            None
        }
    }

    /// Returns this nsCSSValue value as a floating point value, unchecked in
    /// release builds.
    pub fn float_unchecked(&self) -> f32 {
        debug_assert!(nsCSSUnit::eCSSUnit_Number as u32 <= self.mUnit as u32);
        unsafe { *self.mValue.mFloat.as_ref() }
    }

    /// Returns this nsCSSValue as a nsCSSValue::Array, unchecked in release
    /// builds.
    pub unsafe fn array_unchecked(&self) -> &nsCSSValue_Array {
        debug_assert!(
            nsCSSUnit::eCSSUnit_Array as u32 <= self.mUnit as u32 &&
                self.mUnit as u32 <= nsCSSUnit::eCSSUnit_Calc_Plus as u32
        );
        let array = *self.mValue.mArray.as_ref();
        debug_assert!(!array.is_null());
        &*array
    }

    /// Sets LengthOrPercentage value to this nsCSSValue.
    pub unsafe fn set_lop(&mut self, lop: LengthOrPercentage) {
        match lop {
            LengthOrPercentage::Length(px) => self.set_px(px.px()),
            LengthOrPercentage::Percentage(pc) => self.set_percentage(pc.0),
            LengthOrPercentage::Calc(calc) => bindings::Gecko_CSSValue_SetCalc(self, calc.into()),
        }
    }

    /// Sets a px value to this nsCSSValue.
    pub unsafe fn set_px(&mut self, px: f32) {
        bindings::Gecko_CSSValue_SetPixelLength(self, px)
    }

    /// Sets a percentage value to this nsCSSValue.
    pub unsafe fn set_percentage(&mut self, unit_value: f32) {
        bindings::Gecko_CSSValue_SetPercentage(self, unit_value)
    }

    /// Returns LengthOrPercentage value.
    pub unsafe fn get_lop(&self) -> LengthOrPercentage {
        match self.mUnit {
            nsCSSUnit::eCSSUnit_Pixel => {
                LengthOrPercentage::Length(Length::new(bindings::Gecko_CSSValue_GetNumber(self)))
            },
            nsCSSUnit::eCSSUnit_Percent => LengthOrPercentage::Percentage(Percentage(
                bindings::Gecko_CSSValue_GetPercentage(self),
            )),
            nsCSSUnit::eCSSUnit_Calc => {
                LengthOrPercentage::Calc(bindings::Gecko_CSSValue_GetCalc(self).into())
            },
            _ => panic!("Unexpected unit"),
        }
    }

    /// Returns Length  value.
    pub unsafe fn get_length(&self) -> Length {
        match self.mUnit {
            nsCSSUnit::eCSSUnit_Pixel => Length::new(bindings::Gecko_CSSValue_GetNumber(self)),
            _ => panic!("Unexpected unit"),
        }
    }

    fn set_valueless_unit(&mut self, unit: nsCSSUnit) {
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
        debug_assert!(
            unit as u32 <= nsCSSUnit::eCSSUnit_DummyInherit as u32,
            "Not a valueless unit"
        );
        self.mUnit = unit;
    }

    /// Set to an auto value
    ///
    /// This method requires the current value to be null.
    pub fn set_auto(&mut self) {
        self.set_valueless_unit(nsCSSUnit::eCSSUnit_Auto);
    }

    /// Set to a normal value
    ///
    /// This method requires the current value to be null.
    pub fn set_normal(&mut self) {
        self.set_valueless_unit(nsCSSUnit::eCSSUnit_Normal);
    }

    fn set_string_internal(&mut self, s: &str, unit: nsCSSUnit) {
        unsafe { bindings::Gecko_CSSValue_SetString(self, s.as_ptr(), s.len() as u32, unit) }
    }

    fn set_string_from_atom_internal(&mut self, s: &Atom, unit: nsCSSUnit) {
        unsafe { bindings::Gecko_CSSValue_SetStringFromAtom(self, s.as_ptr(), unit) }
    }

    /// Set to a string value
    pub fn set_string(&mut self, s: &str) {
        self.set_string_internal(s, nsCSSUnit::eCSSUnit_String)
    }

    /// Set to a string value from the given atom
    pub fn set_string_from_atom(&mut self, s: &Atom) {
        self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_String)
    }

    /// Set to a ident value from the given atom
    pub fn set_ident_from_atom(&mut self, s: &Atom) {
        self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_Ident)
    }

    /// Set to an identifier value
    pub fn set_ident(&mut self, s: &str) {
        self.set_string_internal(s, nsCSSUnit::eCSSUnit_Ident)
    }

    /// Set to an atom identifier value
    pub fn set_atom_ident(&mut self, s: Atom) {
        unsafe { bindings::Gecko_CSSValue_SetAtomIdent(self, s.into_addrefed()) }
    }

    fn set_int_internal(&mut self, value: i32, unit: nsCSSUnit) {
        unsafe { bindings::Gecko_CSSValue_SetInt(self, value, unit) }
    }

    /// Set to an integer value
    pub fn set_integer(&mut self, value: i32) {
        self.set_int_internal(value, nsCSSUnit::eCSSUnit_Integer)
    }

    /// Set to an enumerated value
    pub fn set_enum<T: Into<i32>>(&mut self, value: T) {
        self.set_int_internal(value.into(), nsCSSUnit::eCSSUnit_Enumerated);
    }

    /// Set to a number value
    pub fn set_number(&mut self, number: f32) {
        unsafe { bindings::Gecko_CSSValue_SetFloat(self, number, nsCSSUnit::eCSSUnit_Number) }
    }

    /// Set to an array of given length
    pub fn set_array(&mut self, len: i32) -> &mut nsCSSValue_Array {
        unsafe { bindings::Gecko_CSSValue_SetArray(self, len) }
        unsafe { self.mValue.mArray.as_mut().as_mut() }.unwrap()
    }

    /// Generic set from any value that implements the ToNsCssValue trait.
    pub fn set_from<T: ToNsCssValue>(&mut self, value: T) {
        value.convert(self)
    }

    /// Returns an `Angle` value from this `nsCSSValue`.
    ///
    /// Panics if the unit is not `eCSSUnit_Degree` `eCSSUnit_Grad`, `eCSSUnit_Turn`
    /// or `eCSSUnit_Radian`.
    pub fn get_angle(&self) -> Angle {
        Angle::from_gecko_values(self.float_unchecked(), self.mUnit)
    }

    /// Sets Angle value to this nsCSSValue.
    pub fn set_angle(&mut self, angle: Angle) {
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
        let (value, unit) = angle.to_gecko_values();
        self.mUnit = unit;
        unsafe {
            *self.mValue.mFloat.as_mut() = value;
        }
    }

    /// Set to a pair value
    ///
    /// This is only supported on the main thread.
    pub fn set_pair(&mut self, x: &nsCSSValue, y: &nsCSSValue) {
        unsafe { bindings::Gecko_CSSValue_SetPair(self, x, y) }
    }

    /// Set to a list value
    ///
    /// This is only supported on the main thread.
    pub fn set_list<I>(&mut self, values: I)
    where
        I: ExactSizeIterator<Item = nsCSSValue>,
    {
        debug_assert!(values.len() > 0, "Empty list is not supported");
        unsafe {
            bindings::Gecko_CSSValue_SetList(self, values.len() as u32);
        }
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_List);
        let list: &mut structs::nsCSSValueList = &mut unsafe {
            self.mValue.mList.as_ref() // &*nsCSSValueList_heap
                .as_mut().expect("List pointer should be non-null")
        }._base;
        for (item, new_value) in list.into_iter().zip(values) {
            *item = new_value;
        }
    }

    /// Set to a pair list value
    ///
    /// This is only supported on the main thread.
    pub fn set_pair_list<I>(&mut self, mut values: I)
    where
        I: ExactSizeIterator<Item = (nsCSSValue, nsCSSValue)>,
    {
        debug_assert!(values.len() > 0, "Empty list is not supported");
        unsafe {
            bindings::Gecko_CSSValue_SetPairList(self, values.len() as u32);
        }
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_PairList);
        let mut item_ptr = &mut unsafe {
            self.mValue.mPairList.as_ref() // &*nsCSSValuePairList_heap
                .as_mut().expect("List pointer should be non-null")
        }._base as *mut structs::nsCSSValuePairList;
        while let Some(item) = unsafe { item_ptr.as_mut() } {
            let value = values.next().expect("Values shouldn't have been exhausted");
            item.mXValue = value.0;
            item.mYValue = value.1;
            item_ptr = item.mNext;
        }
        debug_assert!(values.next().is_none(), "Values should have been exhausted");
    }

    /// Set a shared list
    pub fn set_shared_list<I>(&mut self, values: I)
    where
        I: ExactSizeIterator<Item = nsCSSValue>,
    {
        debug_assert!(values.len() > 0, "Empty list is not supported");
        unsafe { bindings::Gecko_CSSValue_InitSharedList(self, values.len() as u32) };
        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_SharedList);
        let list = unsafe {
            self.mValue
                .mSharedList
                .as_ref()
                .as_mut()
                .expect("List pointer should be non-null")
                .mHead
                .as_mut()
        };
        debug_assert!(list.is_some(), "New created shared list shouldn't be null");
        for (item, new_value) in list.unwrap().into_iter().zip(values) {
            *item = new_value;
        }
    }
}

impl Drop for nsCSSValue {
    fn drop(&mut self) {
        unsafe { bindings::Gecko_CSSValue_Drop(self) };
    }
}

/// Iterator of nsCSSValueList.
#[allow(non_camel_case_types)]
pub struct nsCSSValueListIterator<'a> {
    current: Option<&'a nsCSSValueList>,
}

impl<'a> Iterator for nsCSSValueListIterator<'a> {
    type Item = &'a nsCSSValue;
    fn next(&mut self) -> Option<Self::Item> {
        match self.current {
            Some(item) => {
                self.current = unsafe { item.mNext.as_ref() };
                Some(&item.mValue)
            },
            None => None,
        }
    }
}

impl<'a> IntoIterator for &'a nsCSSValueList {
    type Item = &'a nsCSSValue;
    type IntoIter = nsCSSValueListIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        nsCSSValueListIterator {
            current: Some(self),
        }
    }
}

/// Mutable Iterator of nsCSSValueList.
#[allow(non_camel_case_types)]
pub struct nsCSSValueListMutIterator<'a> {
    current: *mut nsCSSValueList,
    phantom: PhantomData<&'a mut nsCSSValue>,
}

impl<'a> Iterator for nsCSSValueListMutIterator<'a> {
    type Item = &'a mut nsCSSValue;
    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { self.current.as_mut() } {
            Some(item) => {
                self.current = item.mNext;
                Some(&mut item.mValue)
            },
            None => None,
        }
    }
}

impl<'a> IntoIterator for &'a mut nsCSSValueList {
    type Item = &'a mut nsCSSValue;
    type IntoIter = nsCSSValueListMutIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        nsCSSValueListMutIterator {
            current: self as *mut nsCSSValueList,
            phantom: PhantomData,
        }
    }
}

impl nsCSSValue_Array {
    /// Return the length of this `nsCSSValue::Array`
    #[inline]
    pub fn len(&self) -> usize {
        self.mCount
    }

    #[inline]
    fn buffer(&self) -> *const nsCSSValue {
        self.mArray.as_ptr()
    }

    /// Get the array as a slice of nsCSSValues.
    #[inline]
    pub fn as_slice(&self) -> &[nsCSSValue] {
        unsafe { slice::from_raw_parts(self.buffer(), self.len()) }
    }

    /// Get the array as a mutable slice of nsCSSValues.
    #[inline]
    pub fn as_mut_slice(&mut self) -> &mut [nsCSSValue] {
        unsafe { slice::from_raw_parts_mut(self.buffer() as *mut _, self.len()) }
    }
}

impl Index<usize> for nsCSSValue_Array {
    type Output = nsCSSValue;
    #[inline]
    fn index(&self, i: usize) -> &nsCSSValue {
        &self.as_slice()[i]
    }
}

impl IndexMut<usize> for nsCSSValue_Array {
    #[inline]
    fn index_mut(&mut self, i: usize) -> &mut nsCSSValue {
        &mut self.as_mut_slice()[i]
    }
}

/// Generic conversion to nsCSSValue
pub trait ToNsCssValue {
    /// Convert
    fn convert(self, nscssvalue: &mut nsCSSValue);
}

impl<T: ToNsCssValue> From<T> for nsCSSValue {
    fn from(value: T) -> nsCSSValue {
        let mut result = nsCSSValue::null();
        value.convert(&mut result);
        result
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use color::{Color, rgb};
use geometry::Au;
use render_context::RenderContext;
use text::SendableTextRun;

use clone_arc = std::arc::clone;
use geom::Rect;
use geom::{Point2D, Size2D};
use std::arc::ARC;
use servo_net::image::base::Image;
use servo_util::range::Range;

struct DisplayItemData {
    bounds : Rect<Au>, // TODO: whose coordinate system should this use?
}

pub impl DisplayItemData {
    fn new(bounds: &Rect<Au>) -> DisplayItemData {
        DisplayItemData { bounds: copy *bounds }
    }
}

pub enum DisplayItem {
    SolidColor(DisplayItemData, Color),
    // TODO: need to provide spacing data for text run.
    // (i.e, to support rendering of CSS 'word-spacing' and 'letter-spacing')
    // TODO: don't copy text runs, ever.
    Text(DisplayItemData, ~SendableTextRun, Range, Color),
    Image(DisplayItemData, ARC<~Image>),
    Border(DisplayItemData, Au, Color)
}

pub impl<'self> DisplayItem {
    fn d(&'self self) -> &'self DisplayItemData {
        match *self {
            SolidColor(ref d, _) => d,
            Text(ref d, _, _, _) => d,
            Image(ref d, _) => d,
            Border(ref d, _, _) => d
        }
    }

    fn draw_into_context(&self, ctx: &RenderContext) {
        match self {
            &SolidColor(_, color) => {
                ctx.draw_solid_color(&self.d().bounds, color)
            }
            &Text(_, ref run, ref range, color) => {
                debug!("drawing text at %?", self.d().bounds);
                let new_run = @run.deserialize(ctx.font_ctx);
                let font = new_run.font;
                let origin = self.d().bounds.origin;
                let baseline_origin = Point2D(origin.x, origin.y + font.metrics.ascent);
                font.draw_text_into_context(ctx, new_run, range, baseline_origin, color);
                if(new_run.underline){
                    let width = self.d().bounds.size.width;
                    let offset = font.metrics.underline_offset;
                    let u_size = font.metrics.underline_size;
                    let u_bounds = Rect(
                        Point2D(baseline_origin.x, baseline_origin.y),
                        Size2D(width, u_size)
                    );
                    ctx.draw_solid_color(&u_bounds, color);
                }
            },
            &Image(_, ref img) => {
                debug!("drawing image at %?", self.d().bounds);
                ctx.draw_image(self.d().bounds, clone_arc(img));
            }
            &Border(_, width, color) => {
                ctx.draw_border(&self.d().bounds, width, color)
            }
        }

        debug!("%?", {
        ctx.draw_border(&self.d().bounds, Au::from_px(1), rgb(150, 150, 150));
        () });
    }

    fn new_SolidColor(bounds: &Rect<Au>, color: Color) -> DisplayItem {
        SolidColor(DisplayItemData::new(bounds), color)
    }

    fn new_Border(bounds: &Rect<Au>, width: Au, color: Color) -> DisplayItem {
        Border(DisplayItemData::new(bounds), width, color)
    }

    fn new_Text(bounds: &Rect<Au>,
                run: ~SendableTextRun,
                range: Range,
                color: Color) -> DisplayItem {
        Text(DisplayItemData::new(bounds), run, range, color)
    }

    // ARC should be cloned into ImageData, but Images are not sendable
    fn new_Image(bounds: &Rect<Au>, image: ARC<~Image>) -> DisplayItem {
        Image(DisplayItemData::new(bounds), image)
    }
}

// Dual-mode/freezable.
pub struct DisplayList {
    list: ~[~DisplayItem]
}

pub impl DisplayList {
    fn new() -> DisplayList {
        DisplayList { list: ~[] }
    }

    fn append_item(&mut self, item: ~DisplayItem) {
        // FIXME(Issue #150): crashes
        //debug!("Adding display item %u: %?", self.len(), item);
        self.list.push(item);
    }

    fn draw_into_context(&self, ctx: &RenderContext) {
        debug!("beginning display list");
        for self.list.each |item| {
            // FIXME(Issue #150): crashes
            //debug!("drawing %?", *item);
            item.draw_into_context(ctx);
        }
        debug!("ending display list");
    }
}
<commit_msg>Clean up code a little<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use color::{Color, rgb};
use geometry::Au;
use render_context::RenderContext;
use text::SendableTextRun;

use clone_arc = std::arc::clone;
use geom::Rect;
use geom::{Point2D, Size2D};
use std::arc::ARC;
use servo_net::image::base::Image;
use servo_util::range::Range;

struct DisplayItemData {
    bounds : Rect<Au>, // TODO: whose coordinate system should this use?
}

pub impl DisplayItemData {
    fn new(bounds: &Rect<Au>) -> DisplayItemData {
        DisplayItemData { bounds: copy *bounds }
    }
}

pub enum DisplayItem {
    SolidColor(DisplayItemData, Color),
    // TODO: need to provide spacing data for text run.
    // (i.e, to support rendering of CSS 'word-spacing' and 'letter-spacing')
    // TODO: don't copy text runs, ever.
    Text(DisplayItemData, ~SendableTextRun, Range, Color),
    Image(DisplayItemData, ARC<~Image>),
    Border(DisplayItemData, Au, Color)
}

pub impl<'self> DisplayItem {
    fn d(&'self self) -> &'self DisplayItemData {
        match *self {
            SolidColor(ref d, _) => d,
            Text(ref d, _, _, _) => d,
            Image(ref d, _) => d,
            Border(ref d, _, _) => d
        }
    }

    fn draw_into_context(&self, ctx: &RenderContext) {
        match self {
            &SolidColor(_, color) => {
                ctx.draw_solid_color(&self.d().bounds, color)
            }
            &Text(_, ref run, ref range, color) => {
                debug!("drawing text at %?", self.d().bounds);
                let new_run = @run.deserialize(ctx.font_ctx);
                let font = new_run.font;
                let origin = self.d().bounds.origin;
                let baseline_origin = Point2D(origin.x, origin.y + font.metrics.ascent);
                font.draw_text_into_context(ctx, new_run, range, baseline_origin, color);
                if(new_run.underline){
                    //TODO: Use the font metrics to properly position the underline bar
                    let width = self.d().bounds.size.width;
                    let u_size = font.metrics.underline_size;
                    let u_bounds = Rect(
                        Point2D(baseline_origin.x, baseline_origin.y),
                        Size2D(width, u_size)
                    );
                    ctx.draw_solid_color(&u_bounds, color);
                }
            },
            &Image(_, ref img) => {
                debug!("drawing image at %?", self.d().bounds);
                ctx.draw_image(self.d().bounds, clone_arc(img));
            }
            &Border(_, width, color) => {
                ctx.draw_border(&self.d().bounds, width, color)
            }
        }

        debug!("%?", {
        ctx.draw_border(&self.d().bounds, Au::from_px(1), rgb(150, 150, 150));
        () });
    }

    fn new_SolidColor(bounds: &Rect<Au>, color: Color) -> DisplayItem {
        SolidColor(DisplayItemData::new(bounds), color)
    }

    fn new_Border(bounds: &Rect<Au>, width: Au, color: Color) -> DisplayItem {
        Border(DisplayItemData::new(bounds), width, color)
    }

    fn new_Text(bounds: &Rect<Au>,
                run: ~SendableTextRun,
                range: Range,
                color: Color) -> DisplayItem {
        Text(DisplayItemData::new(bounds), run, range, color)
    }

    // ARC should be cloned into ImageData, but Images are not sendable
    fn new_Image(bounds: &Rect<Au>, image: ARC<~Image>) -> DisplayItem {
        Image(DisplayItemData::new(bounds), image)
    }
}

// Dual-mode/freezable.
pub struct DisplayList {
    list: ~[~DisplayItem]
}

pub impl DisplayList {
    fn new() -> DisplayList {
        DisplayList { list: ~[] }
    }

    fn append_item(&mut self, item: ~DisplayItem) {
        // FIXME(Issue #150): crashes
        //debug!("Adding display item %u: %?", self.len(), item);
        self.list.push(item);
    }

    fn draw_into_context(&self, ctx: &RenderContext) {
        debug!("beginning display list");
        for self.list.each |item| {
            // FIXME(Issue #150): crashes
            //debug!("drawing %?", *item);
            item.draw_into_context(ctx);
        }
        debug!("ending display list");
    }
}
<|endoftext|>