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

use binaryninjacore_sys::*;

use std::result;

use crate::rc::*;
use crate::string::*;

pub type Result<R> = result::Result<R, ()>;

#[derive(PartialEq, Eq, Hash)]
pub struct BackgroundTask {
    pub(crate) handle: *mut BNBackgroundTask,
}

impl BackgroundTask {
    pub unsafe fn from_raw(handle: *mut BNBackgroundTask) -> Self {
        debug_assert!(!handle.is_null());

        Self { handle }
    }

    pub fn new<S: BnStrCompatible>(
        initial_text: S,
        can_cancel: bool
    ) -> Result<Ref<Self>> {
        let text = initial_text.as_bytes_with_nul();

        let handle = unsafe {
            BNBeginBackgroundTask(text.as_ref().as_ptr() as *mut _, can_cancel)
        };

        if handle.is_null() {
            return Err(());
        }

        unsafe { Ok(Ref::new(Self { handle })) }
    }

    pub fn can_cancel(&self) -> bool {
        unsafe { BNCanCancelBackgroundTask(self.handle) }
    }

    pub fn is_cancelled(&self) -> bool {
        unsafe { BNIsBackgroundTaskCancelled(self.handle) }
    }

    pub fn is_finished(&self) -> bool {
        unsafe { BNIsBackgroundTaskFinished(self.handle) }
    }

    pub fn get_progress_text(&self) -> BnString {
        unsafe { BnString::from_raw(BNGetBackgroundTaskProgressText(self.handle)) }
    }

    pub fn cancel(&self) {
        unsafe { BNCancelBackgroundTask(self.handle) }
    }

    pub fn finish(&self) {
        unsafe { BNFinishBackgroundTask(self.handle) }
    }

    pub fn set_progress_text<S: BnStrCompatible>(&self, text: S) {
        let progress_text = text.as_bytes_with_nul();

        unsafe {
            BNSetBackgroundTaskProgressText(
                self.handle,
                progress_text.as_ref().as_ptr() as *mut _
            )
        }
    }
}

unsafe impl RefCountable for BackgroundTask {
    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
        Ref::new(Self {
            handle: BNNewBackgroundTaskReference(handle.handle),
        })
    }

    unsafe fn dec_ref(handle: &Self) {
        BNFreeBackgroundTask(handle.handle);
    }
}

impl AsRef<BackgroundTask> for BackgroundTask {
    fn as_ref(&self) -> &Self {
        self
    }
}

impl ToOwned for BackgroundTask {
    type Owned = Ref<Self>;

    fn to_owned(&self) -> Self::Owned {
        unsafe { RefCountable::inc_ref(self) }
    }
}

unsafe impl Send for BackgroundTask {}
unsafe impl Sync for BackgroundTask {}<commit_msg>Implement BackgroundTask::running_tasks()<commit_after>// Copyright 2021 Vector 35 Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use binaryninjacore_sys::*;

use std::result;

use crate::rc::*;
use crate::string::*;

pub type Result<R> = result::Result<R, ()>;

#[derive(PartialEq, Eq, Hash)]
pub struct BackgroundTask {
    pub(crate) handle: *mut BNBackgroundTask,
}

impl BackgroundTask {
    pub unsafe fn from_raw(handle: *mut BNBackgroundTask) -> Self {
        debug_assert!(!handle.is_null());

        Self { handle }
    }

    pub fn new<S: BnStrCompatible>(
        initial_text: S,
        can_cancel: bool
    ) -> Result<Ref<Self>> {
        let text = initial_text.as_bytes_with_nul();

        let handle = unsafe {
            BNBeginBackgroundTask(text.as_ref().as_ptr() as *mut _, can_cancel)
        };

        if handle.is_null() {
            return Err(());
        }

        unsafe { Ok(Ref::new(Self { handle })) }
    }

    pub fn can_cancel(&self) -> bool {
        unsafe { BNCanCancelBackgroundTask(self.handle) }
    }

    pub fn is_cancelled(&self) -> bool {
        unsafe { BNIsBackgroundTaskCancelled(self.handle) }
    }

    pub fn is_finished(&self) -> bool {
        unsafe { BNIsBackgroundTaskFinished(self.handle) }
    }

    pub fn get_progress_text(&self) -> BnString {
        unsafe { BnString::from_raw(BNGetBackgroundTaskProgressText(self.handle)) }
    }

    pub fn cancel(&self) {
        unsafe { BNCancelBackgroundTask(self.handle) }
    }

    pub fn finish(&self) {
        unsafe { BNFinishBackgroundTask(self.handle) }
    }

    pub fn set_progress_text<S: BnStrCompatible>(&self, text: S) {
        let progress_text = text.as_bytes_with_nul();

        unsafe {
            BNSetBackgroundTaskProgressText(
                self.handle,
                progress_text.as_ref().as_ptr() as *mut _
            )
        }
    }

    pub fn running_tasks() -> Array<BackgroundTask> {
        unsafe {
            let mut count = 0;
            let handles = BNGetRunningBackgroundTasks(&mut count);

            Array::new(handles, count, ())
        }
    }
}

unsafe impl RefCountable for BackgroundTask {
    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
        Ref::new(Self {
            handle: BNNewBackgroundTaskReference(handle.handle),
        })
    }

    unsafe fn dec_ref(handle: &Self) {
        BNFreeBackgroundTask(handle.handle);
    }
}

unsafe impl CoreOwnedArrayProvider for BackgroundTask {
    type Raw = *mut BNBackgroundTask;
    type Context = ();

    unsafe fn free(raw: *mut *mut BNBackgroundTask, count: usize, _context: &()) {
        BNFreeBackgroundTaskList(raw, count);
    }
}

unsafe impl<'a> CoreOwnedArrayWrapper<'a> for BackgroundTask {
    type Wrapped = Guard<'a, BackgroundTask>;

    unsafe fn wrap_raw(raw: &'a *mut BNBackgroundTask, context: &'a ()) -> Guard<'a, BackgroundTask> {
        Guard::new(BackgroundTask::from_raw(*raw), context)
    }
}

impl AsRef<BackgroundTask> for BackgroundTask {
    fn as_ref(&self) -> &Self {
        self
    }
}

impl ToOwned for BackgroundTask {
    type Owned = Ref<Self>;

    fn to_owned(&self) -> Self::Owned {
        unsafe { RefCountable::inc_ref(self) }
    }
}

unsafe impl Send for BackgroundTask {}
unsafe impl Sync for BackgroundTask {}<|endoftext|>


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

use std::{
  fs::{self, File},
  io::Write,
  path::{Path, PathBuf},
};

use anyhow::Result;

use docopt::Docopt;

use cargo_raze::{
  metadata::{CargoMetadataFetcher, CargoWorkspaceFiles, MetadataFetcher},
  planning::{BuildPlanner, BuildPlannerImpl},
  rendering::{bazel::BazelRenderer, BuildRenderer, FileOutputs, RenderDetails},
  settings::{load_settings, GenMode},
  util::{find_bazel_workspace_root, PlatformDetails},
};

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Options {
  arg_buildprefix: Option<String>,
  flag_verbose: u32,
  flag_quiet: Option<bool>,
  flag_host: Option<String>,
  flag_color: Option<String>,
  flag_target: Option<String>,
  flag_dryrun: Option<bool>,
  flag_cargo_bin_path: Option<String>,
  flag_output: String,
}

const USAGE: &str = r#"
Generate BUILD files for your pre-vendored Cargo dependencies.

Usage:
    cargo raze (-h | --help)
    cargo raze [--verbose] [--quiet] [--color=<WHEN>] [--dryrun] [--cargo-bin-path=<PATH>] [--output=<PATH>]
    cargo raze <buildprefix> [--verbose] [--quiet] [--color=<WHEN>] [--dryrun] [--cargo-bin-path=<PATH>]
                             [--output=<PATH>]

Options:
    -h, --help                         Print this message
    -v, --verbose                      Use verbose output
    -q, --quiet                        No output printed to stdout
    --color=<WHEN>                     Coloring: auto, always, never
    -d, --dryrun                       Do not emit any files
    --cargo-bin-path=<PATH>            Path to the cargo binary to be used for loading workspace metadata
    --output=<PATH>                    Path to output the generated into.
"#;

fn main() -> Result<()> {
  let options: Options = Docopt::new(USAGE)
    .and_then(|d| d.deserialize())
    .unwrap_or_else(|e| e.exit());

  let settings = load_settings("Cargo.toml")?;
  println!("Loaded override settings: {:#?}", settings);

  let mut metadata_fetcher: Box<dyn MetadataFetcher> = match options.flag_cargo_bin_path {
    Some(ref p) => Box::new(CargoMetadataFetcher::new(p, /*use_tempdir: */ true)),
    None => Box::new(CargoMetadataFetcher::default()),
  };
  let mut planner = BuildPlannerImpl::new(&mut *metadata_fetcher);

  let toml_path = PathBuf::from("./Cargo.toml");
  let lock_path_opt = fs::metadata("./Cargo.lock")
    .ok()
    .map(|_| PathBuf::from("./Cargo.lock"));
  let files = CargoWorkspaceFiles {
    toml_path,
    lock_path_opt,
  };

  let platform_details = match &settings.target {
    Some(target) => Some(PlatformDetails::new_using_rustc(target)?),
    None => None,
  };

  let prefix_path = calculate_workspace_root(
    &settings.workspace_path,
    match !options.flag_output.is_empty() {
      true => Some(&options.flag_output),
      false => None,
    },
    settings.incompatible_relative_workspace_path,
  )?;

  let planned_build = planner.plan_build(&settings, &prefix_path, files, platform_details)?;
  let mut bazel_renderer = BazelRenderer::new();

  let render_details = RenderDetails {
    path_prefix: prefix_path,
    buildfile_suffix: settings.output_buildfile_suffix,
  };

  let dry_run = options.flag_dryrun.unwrap_or(false);
  if !dry_run {
    fs::create_dir_all(&render_details.path_prefix)?;
  }

  let bazel_file_outputs = match settings.genmode {
    GenMode::Vendored => bazel_renderer.render_planned_build(&render_details, &planned_build)?,
    GenMode::Remote => {
      if !dry_run {
        // Create the "remote" directory if it doesn't exist
        fs::create_dir_all(render_details.path_prefix.as_path().join("remote"))?;

        if !settings.binary_deps.is_empty() {
          fs::create_dir_all(
            render_details
              .path_prefix
              .clone()
              .as_path()
              .join("lockfiles"),
          )?;
        }
      }

      bazel_renderer.render_remote_planned_build(&render_details, &planned_build)?
    }, /* exhaustive, we control the definition */
  };

  for FileOutputs {
    path,
    contents,
  } in bazel_file_outputs
  {
    if dry_run {
      println!("{}:\n{}", path.display(), contents);
    } else {
      write_to_file_loudly(&path, &contents)?;
    }
  }

  Ok(())
}

fn write_to_file_loudly(path: &Path, contents: &str) -> Result<()> {
  File::create(&path).and_then(|mut f| f.write_all(contents.as_bytes()))?;
  println!("Generated {} successfully", path.display());
  Ok(())
}

fn calculate_workspace_root(
  path: &str,
  output_override: Option<&str>,
  new_behavior: bool,
) -> Result<PathBuf> {
  // Default to the current directory '.'
  let mut prefix_path: PathBuf = std::env::current_dir()?;

  // Allow the command line option to take precedence
  match output_override {
    Some(output) => {
      prefix_path.clear();
      prefix_path.push(output);
    },
    None => {
      if new_behavior {
        if let Some(workspace_root) = find_bazel_workspace_root() {
          prefix_path.clear();
          prefix_path.push(workspace_root);
          prefix_path.push(
            path
              // Remove the leading "//" from the path
              .trim_start_matches('/'),
          );
        }
      }
    },
  }

  Ok(prefix_path)
}

#[test]
fn test_calculate_workspace_root() {
  assert_eq!(
    calculate_workspace_root(&"path_a".to_string(), None, true).unwrap(),
    std::env::current_dir().unwrap()
  );

  assert_eq!(
    calculate_workspace_root(
      &"path_b".to_string(),
      Some(&"direct/path".to_string()),
      true
    )
    .unwrap(),
    std::path::Path::new("direct").join("path")
  );
}
<commit_msg>Gated loud output behind the `--verbose` flag. (#263)<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{
  fs::{self, File},
  io::Write,
  path::{Path, PathBuf},
};

use anyhow::Result;

use docopt::Docopt;

use cargo_raze::{
  metadata::{CargoMetadataFetcher, CargoWorkspaceFiles, MetadataFetcher},
  planning::{BuildPlanner, BuildPlannerImpl},
  rendering::{bazel::BazelRenderer, BuildRenderer, FileOutputs, RenderDetails},
  settings::{load_settings, GenMode},
  util::{find_bazel_workspace_root, PlatformDetails},
};

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Options {
  arg_buildprefix: Option<String>,
  flag_verbose: u32,
  flag_quiet: Option<bool>,
  flag_host: Option<String>,
  flag_color: Option<String>,
  flag_target: Option<String>,
  flag_dryrun: Option<bool>,
  flag_cargo_bin_path: Option<String>,
  flag_output: String,
}

const USAGE: &str = r#"
Generate BUILD files for your pre-vendored Cargo dependencies.

Usage:
    cargo raze (-h | --help)
    cargo raze [--verbose] [--quiet] [--color=<WHEN>] [--dryrun] [--cargo-bin-path=<PATH>] [--output=<PATH>]
    cargo raze <buildprefix> [--verbose] [--quiet] [--color=<WHEN>] [--dryrun] [--cargo-bin-path=<PATH>]
                             [--output=<PATH>]

Options:
    -h, --help                         Print this message
    -v, --verbose                      Use verbose output
    -q, --quiet                        No output printed to stdout
    --color=<WHEN>                     Coloring: auto, always, never
    -d, --dryrun                       Do not emit any files
    --cargo-bin-path=<PATH>            Path to the cargo binary to be used for loading workspace metadata
    --output=<PATH>                    Path to output the generated into.
"#;

fn main() -> Result<()> {
  let options: Options = Docopt::new(USAGE)
    .and_then(|d| d.deserialize())
    .unwrap_or_else(|e| e.exit());

  let settings = load_settings("Cargo.toml")?;
  if options.flag_verbose > 0 {
    println!("Loaded override settings: {:#?}", settings);
  }

  let mut metadata_fetcher: Box<dyn MetadataFetcher> = match options.flag_cargo_bin_path {
    Some(ref p) => Box::new(CargoMetadataFetcher::new(p, /*use_tempdir: */ true)),
    None => Box::new(CargoMetadataFetcher::default()),
  };
  let mut planner = BuildPlannerImpl::new(&mut *metadata_fetcher);

  let toml_path = PathBuf::from("./Cargo.toml");
  let lock_path_opt = fs::metadata("./Cargo.lock")
    .ok()
    .map(|_| PathBuf::from("./Cargo.lock"));
  let files = CargoWorkspaceFiles {
    toml_path,
    lock_path_opt,
  };

  let platform_details = match &settings.target {
    Some(target) => Some(PlatformDetails::new_using_rustc(target)?),
    None => None,
  };

  let prefix_path = calculate_workspace_root(
    &settings.workspace_path,
    match !options.flag_output.is_empty() {
      true => Some(&options.flag_output),
      false => None,
    },
    settings.incompatible_relative_workspace_path,
  )?;

  let planned_build = planner.plan_build(&settings, &prefix_path, files, platform_details)?;
  let mut bazel_renderer = BazelRenderer::new();

  let render_details = RenderDetails {
    path_prefix: prefix_path,
    buildfile_suffix: settings.output_buildfile_suffix,
  };

  let dry_run = options.flag_dryrun.unwrap_or(false);
  if !dry_run {
    fs::create_dir_all(&render_details.path_prefix)?;
  }

  let bazel_file_outputs = match settings.genmode {
    GenMode::Vendored => bazel_renderer.render_planned_build(&render_details, &planned_build)?,
    GenMode::Remote => {
      if !dry_run {
        // Create the "remote" directory if it doesn't exist
        fs::create_dir_all(render_details.path_prefix.as_path().join("remote"))?;

        if !settings.binary_deps.is_empty() {
          fs::create_dir_all(
            render_details
              .path_prefix
              .clone()
              .as_path()
              .join("lockfiles"),
          )?;
        }
      }

      bazel_renderer.render_remote_planned_build(&render_details, &planned_build)?
    }, /* exhaustive, we control the definition */
  };

  for FileOutputs {
    path,
    contents,
  } in bazel_file_outputs
  {
    if dry_run {
      println!("{}:\n{}", path.display(), contents);
    } else {
      write_to_file(&path, &contents, options.flag_verbose > 0)?;
    }
  }

  Ok(())
}

fn write_to_file(path: &Path, contents: &str, verbose: bool) -> Result<()> {
  File::create(&path).and_then(|mut f| f.write_all(contents.as_bytes()))?;
  if verbose {
    println!("Generated {} successfully", path.display());
  }
  Ok(())
}

fn calculate_workspace_root(
  path: &str,
  output_override: Option<&str>,
  new_behavior: bool,
) -> Result<PathBuf> {
  // Default to the current directory '.'
  let mut prefix_path: PathBuf = std::env::current_dir()?;

  // Allow the command line option to take precedence
  match output_override {
    Some(output) => {
      prefix_path.clear();
      prefix_path.push(output);
    },
    None => {
      if new_behavior {
        if let Some(workspace_root) = find_bazel_workspace_root() {
          prefix_path.clear();
          prefix_path.push(workspace_root);
          prefix_path.push(
            path
              // Remove the leading "//" from the path
              .trim_start_matches('/'),
          );
        }
      }
    },
  }

  Ok(prefix_path)
}

#[test]
fn test_calculate_workspace_root() {
  assert_eq!(
    calculate_workspace_root(&"path_a".to_string(), None, true).unwrap(),
    std::env::current_dir().unwrap()
  );

  assert_eq!(
    calculate_workspace_root(
      &"path_b".to_string(),
      Some(&"direct/path".to_string()),
      true
    )
    .unwrap(),
    std::path::Path::new("direct").join("path")
  );
}
<|endoftext|>


<commit_before>//! This module handles fuzzy-searching of functions, structs and other symbols
//! by name across the whole workspace and dependencies.
//!
//! It works by building an incrementally-updated text-search index of all
//! symbols. The backbone of the index is the **awesome** `fst` crate by
//! @BurntSushi.
//!
//! In a nutshell, you give a set of strings to `fst`, and it builds a
//! finite state machine describing this set of strings. The strings which
//! could fuzzy-match a pattern can also be described by a finite state machine.
//! What is freaking cool is that you can now traverse both state machines in
//! lock-step to enumerate the strings which are both in the input set and
//! fuzz-match the query. Or, more formally, given two languages described by
//! FSTs, one can build a product FST which describes the intersection of the
//! languages.
//!
//! `fst` does not support cheap updating of the index, but it supports unioning
//! of state machines. So, to account for changing source code, we build an FST
//! for each library (which is assumed to never change) and an FST for each Rust
//! file in the current workspace, and run a query against the union of all
//! those FSTs.

use std::{
    cmp::Ordering,
    fmt,
    hash::{Hash, Hasher},
    mem,
    sync::Arc,
};

use fst::{self, Streamer};
use hir::db::DefDatabase;
use ra_db::{
    salsa::{self, ParallelDatabase},
    CrateId, FileId, SourceDatabaseExt, SourceRootId,
};
use ra_syntax::{
    ast::{self, NameOwner},
    match_ast, AstNode, Parse, SmolStr, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent,
};
#[cfg(not(feature = "wasm"))]
use rayon::prelude::*;

use crate::RootDatabase;

#[derive(Debug)]
pub struct Query {
    query: String,
    lowercased: String,
    only_types: bool,
    libs: bool,
    exact: bool,
    limit: usize,
}

impl Query {
    pub fn new(query: String) -> Query {
        let lowercased = query.to_lowercase();
        Query {
            query,
            lowercased,
            only_types: false,
            libs: false,
            exact: false,
            limit: usize::max_value(),
        }
    }

    pub fn only_types(&mut self) {
        self.only_types = true;
    }

    pub fn libs(&mut self) {
        self.libs = true;
    }

    pub fn exact(&mut self) {
        self.exact = true;
    }

    pub fn limit(&mut self, limit: usize) {
        self.limit = limit
    }
}

#[salsa::query_group(SymbolsDatabaseStorage)]
pub trait SymbolsDatabase: hir::db::HirDatabase {
    fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>;
    #[salsa::input]
    fn library_symbols(&self, id: SourceRootId) -> Arc<SymbolIndex>;
    /// The set of "local" (that is, from the current workspace) roots.
    /// Files in local roots are assumed to change frequently.
    #[salsa::input]
    fn local_roots(&self) -> Arc<Vec<SourceRootId>>;
    /// The set of roots for crates.io libraries.
    /// Files in libraries are assumed to never change.
    #[salsa::input]
    fn library_roots(&self) -> Arc<Vec<SourceRootId>>;
}

fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
    db.check_canceled();
    let parse = db.parse(file_id);

    let symbols = source_file_to_file_symbols(&parse.tree(), file_id);

    // FIXME: add macros here

    Arc::new(SymbolIndex::new(symbols))
}

/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
struct Snap(salsa::Snapshot<RootDatabase>);
impl Clone for Snap {
    fn clone(&self) -> Snap {
        Snap(self.0.snapshot())
    }
}

// Feature: Workspace Symbol
//
// Uses fuzzy-search to find types, modules and functions by name across your
// project and dependencies. This is **the** most useful feature, which improves code
// navigation tremendously. It mostly works on top of the built-in LSP
// functionality, however `#` and `*` symbols can be used to narrow down the
// search. Specifically,
//
// - `Foo` searches for `Foo` type in the current workspace
// - `foo#` searches for `foo` function in the current workspace
// - `Foo*` searches for `Foo` type among dependencies, including `stdlib`
// - `foo#*` searches for `foo` function among dependencies
//
// That is, `#` switches from "types" to all symbols, `*` switches from the current
// workspace to dependencies.
//
// |===
// | Editor  | Shortcut
//
// | VS Code | kbd:[Ctrl+T]
// |===
pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
    let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());

    let buf: Vec<Arc<SymbolIndex>> = if query.libs {
        let snap = Snap(db.snapshot());
        #[cfg(not(feature = "wasm"))]
        let buf = db
            .library_roots()
            .par_iter()
            .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
            .collect();

        #[cfg(feature = "wasm")]
        let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect();

        buf
    } else {
        let mut files = Vec::new();
        for &root in db.local_roots().iter() {
            let sr = db.source_root(root);
            files.extend(sr.walk())
        }

        let snap = Snap(db.snapshot());
        #[cfg(not(feature = "wasm"))]
        let buf =
            files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect();

        #[cfg(feature = "wasm")]
        let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect();

        buf
    };
    query.search(&buf)
}

pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
    let def_map = db.crate_def_map(krate);
    let mut files = Vec::new();
    let mut modules = vec![def_map.root];
    while let Some(module) = modules.pop() {
        let data = &def_map[module];
        files.extend(data.origin.file_id());
        modules.extend(data.children.values());
    }

    let snap = Snap(db.snapshot());

    #[cfg(not(feature = "wasm"))]
    let buf = files
        .par_iter()
        .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
        .collect::<Vec<_>>();

    #[cfg(feature = "wasm")]
    let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::<Vec<_>>();

    query.search(&buf)
}

pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
    let name = name_ref.text();
    let mut query = Query::new(name.to_string());
    query.exact();
    query.limit(4);
    world_symbols(db, query)
}

#[derive(Default)]
pub struct SymbolIndex {
    symbols: Vec<FileSymbol>,
    map: fst::Map<Vec<u8>>,
}

impl fmt::Debug for SymbolIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish()
    }
}

impl PartialEq for SymbolIndex {
    fn eq(&self, other: &SymbolIndex) -> bool {
        self.symbols == other.symbols
    }
}

impl Eq for SymbolIndex {}

impl Hash for SymbolIndex {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        self.symbols.hash(hasher)
    }
}

impl SymbolIndex {
    fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
        fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
            let lhs_chars = lhs.name.chars().map(|c| c.to_ascii_lowercase());
            let rhs_chars = rhs.name.chars().map(|c| c.to_ascii_lowercase());
            lhs_chars.cmp(rhs_chars)
        }

        #[cfg(not(feature = "wasm"))]
        symbols.par_sort_by(cmp);

        #[cfg(feature = "wasm")]
        symbols.sort_by(cmp);

        let mut builder = fst::MapBuilder::memory();

        let mut last_batch_start = 0;

        for idx in 0..symbols.len() {
            if let Some(next_symbol) = symbols.get(idx + 1) {
                if cmp(&symbols[last_batch_start], next_symbol) == Ordering::Equal {
                    continue;
                }
            }

            let start = last_batch_start;
            let end = idx + 1;
            last_batch_start = end;

            let key = symbols[start].name.as_str().to_ascii_lowercase();
            let value = SymbolIndex::range_to_map_value(start, end);

            builder.insert(key, value).unwrap();
        }

        let map = fst::Map::new(builder.into_inner().unwrap()).unwrap();
        SymbolIndex { symbols, map }
    }

    pub fn len(&self) -> usize {
        self.symbols.len()
    }

    pub fn memory_size(&self) -> usize {
        self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
    }

    #[cfg(not(feature = "wasm"))]
    pub(crate) fn for_files(
        files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
    ) -> SymbolIndex {
        let symbols = files
            .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
            .collect::<Vec<_>>();
        SymbolIndex::new(symbols)
    }

    #[cfg(feature = "wasm")]
    pub(crate) fn for_files(
        files: impl Iterator<Item = (FileId, Parse<ast::SourceFile>)>,
    ) -> SymbolIndex {
        let symbols = files
            .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
            .collect::<Vec<_>>();
        SymbolIndex::new(symbols)
    }

    fn range_to_map_value(start: usize, end: usize) -> u64 {
        debug_assert![start <= (std::u32::MAX as usize)];
        debug_assert![end <= (std::u32::MAX as usize)];

        ((start as u64) << 32) | end as u64
    }

    fn map_value_to_range(value: u64) -> (usize, usize) {
        let end = value as u32 as usize;
        let start = (value >> 32) as usize;
        (start, end)
    }
}

impl Query {
    pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<FileSymbol> {
        let mut op = fst::map::OpBuilder::new();
        for file_symbols in indices.iter() {
            let automaton = fst::automaton::Subsequence::new(&self.lowercased);
            op = op.add(file_symbols.map.search(automaton))
        }
        let mut stream = op.union();
        let mut res = Vec::new();
        while let Some((_, indexed_values)) = stream.next() {
            for indexed_value in indexed_values {
                let symbol_index = &indices[indexed_value.index];
                let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);

                for symbol in &symbol_index.symbols[start..end] {
                    if self.only_types && !is_type(symbol.kind) {
                        continue;
                    }
                    if self.exact && symbol.name != self.query {
                        continue;
                    }

                    if res.len() >= self.limit {
                        return res;
                    }
                    res.push(symbol.clone());
                }
            }
        }
        res
    }
}

fn is_type(kind: SyntaxKind) -> bool {
    match kind {
        STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => true,
        _ => false,
    }
}

/// The actual data that is stored in the index. It should be as compact as
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol {
    pub file_id: FileId,
    pub name: SmolStr,
    pub kind: SyntaxKind,
    pub range: TextRange,
    pub ptr: SyntaxNodePtr,
    pub name_range: Option<TextRange>,
    pub container_name: Option<SmolStr>,
}

fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec<FileSymbol> {
    let mut symbols = Vec::new();
    let mut stack = Vec::new();

    for event in source_file.syntax().preorder() {
        match event {
            WalkEvent::Enter(node) => {
                if let Some(mut symbol) = to_file_symbol(&node, file_id) {
                    symbol.container_name = stack.last().cloned();

                    stack.push(symbol.name.clone());
                    symbols.push(symbol);
                }
            }

            WalkEvent::Leave(node) => {
                if to_symbol(&node).is_some() {
                    stack.pop();
                }
            }
        }
    }

    symbols
}

fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
    fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
        let name = node.name()?;
        let name_range = name.syntax().text_range();
        let name = name.text().clone();
        let ptr = SyntaxNodePtr::new(node.syntax());

        Some((name, ptr, name_range))
    }
    match_ast! {
        match node {
            ast::FnDef(it) => decl(it),
            ast::StructDef(it) => decl(it),
            ast::EnumDef(it) => decl(it),
            ast::TraitDef(it) => decl(it),
            ast::Module(it) => decl(it),
            ast::TypeAliasDef(it) => decl(it),
            ast::ConstDef(it) => decl(it),
            ast::StaticDef(it) => decl(it),
            ast::MacroCall(it) => {
                if it.is_macro_rules().is_some() {
                    decl(it)
                } else {
                    None
                }
            },
            _ => None,
        }
    }
}

fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
    to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol {
        name,
        kind: node.kind(),
        range: node.text_range(),
        ptr,
        file_id,
        name_range: Some(name_range),
        container_name: None,
    })
}
<commit_msg>Move limit check down<commit_after>//! This module handles fuzzy-searching of functions, structs and other symbols
//! by name across the whole workspace and dependencies.
//!
//! It works by building an incrementally-updated text-search index of all
//! symbols. The backbone of the index is the **awesome** `fst` crate by
//! @BurntSushi.
//!
//! In a nutshell, you give a set of strings to `fst`, and it builds a
//! finite state machine describing this set of strings. The strings which
//! could fuzzy-match a pattern can also be described by a finite state machine.
//! What is freaking cool is that you can now traverse both state machines in
//! lock-step to enumerate the strings which are both in the input set and
//! fuzz-match the query. Or, more formally, given two languages described by
//! FSTs, one can build a product FST which describes the intersection of the
//! languages.
//!
//! `fst` does not support cheap updating of the index, but it supports unioning
//! of state machines. So, to account for changing source code, we build an FST
//! for each library (which is assumed to never change) and an FST for each Rust
//! file in the current workspace, and run a query against the union of all
//! those FSTs.

use std::{
    cmp::Ordering,
    fmt,
    hash::{Hash, Hasher},
    mem,
    sync::Arc,
};

use fst::{self, Streamer};
use hir::db::DefDatabase;
use ra_db::{
    salsa::{self, ParallelDatabase},
    CrateId, FileId, SourceDatabaseExt, SourceRootId,
};
use ra_syntax::{
    ast::{self, NameOwner},
    match_ast, AstNode, Parse, SmolStr, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent,
};
#[cfg(not(feature = "wasm"))]
use rayon::prelude::*;

use crate::RootDatabase;

#[derive(Debug)]
pub struct Query {
    query: String,
    lowercased: String,
    only_types: bool,
    libs: bool,
    exact: bool,
    limit: usize,
}

impl Query {
    pub fn new(query: String) -> Query {
        let lowercased = query.to_lowercase();
        Query {
            query,
            lowercased,
            only_types: false,
            libs: false,
            exact: false,
            limit: usize::max_value(),
        }
    }

    pub fn only_types(&mut self) {
        self.only_types = true;
    }

    pub fn libs(&mut self) {
        self.libs = true;
    }

    pub fn exact(&mut self) {
        self.exact = true;
    }

    pub fn limit(&mut self, limit: usize) {
        self.limit = limit
    }
}

#[salsa::query_group(SymbolsDatabaseStorage)]
pub trait SymbolsDatabase: hir::db::HirDatabase {
    fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>;
    #[salsa::input]
    fn library_symbols(&self, id: SourceRootId) -> Arc<SymbolIndex>;
    /// The set of "local" (that is, from the current workspace) roots.
    /// Files in local roots are assumed to change frequently.
    #[salsa::input]
    fn local_roots(&self) -> Arc<Vec<SourceRootId>>;
    /// The set of roots for crates.io libraries.
    /// Files in libraries are assumed to never change.
    #[salsa::input]
    fn library_roots(&self) -> Arc<Vec<SourceRootId>>;
}

fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
    db.check_canceled();
    let parse = db.parse(file_id);

    let symbols = source_file_to_file_symbols(&parse.tree(), file_id);

    // FIXME: add macros here

    Arc::new(SymbolIndex::new(symbols))
}

/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
struct Snap(salsa::Snapshot<RootDatabase>);
impl Clone for Snap {
    fn clone(&self) -> Snap {
        Snap(self.0.snapshot())
    }
}

// Feature: Workspace Symbol
//
// Uses fuzzy-search to find types, modules and functions by name across your
// project and dependencies. This is **the** most useful feature, which improves code
// navigation tremendously. It mostly works on top of the built-in LSP
// functionality, however `#` and `*` symbols can be used to narrow down the
// search. Specifically,
//
// - `Foo` searches for `Foo` type in the current workspace
// - `foo#` searches for `foo` function in the current workspace
// - `Foo*` searches for `Foo` type among dependencies, including `stdlib`
// - `foo#*` searches for `foo` function among dependencies
//
// That is, `#` switches from "types" to all symbols, `*` switches from the current
// workspace to dependencies.
//
// |===
// | Editor  | Shortcut
//
// | VS Code | kbd:[Ctrl+T]
// |===
pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
    let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());

    let buf: Vec<Arc<SymbolIndex>> = if query.libs {
        let snap = Snap(db.snapshot());
        #[cfg(not(feature = "wasm"))]
        let buf = db
            .library_roots()
            .par_iter()
            .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
            .collect();

        #[cfg(feature = "wasm")]
        let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect();

        buf
    } else {
        let mut files = Vec::new();
        for &root in db.local_roots().iter() {
            let sr = db.source_root(root);
            files.extend(sr.walk())
        }

        let snap = Snap(db.snapshot());
        #[cfg(not(feature = "wasm"))]
        let buf =
            files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect();

        #[cfg(feature = "wasm")]
        let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect();

        buf
    };
    query.search(&buf)
}

pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
    let def_map = db.crate_def_map(krate);
    let mut files = Vec::new();
    let mut modules = vec![def_map.root];
    while let Some(module) = modules.pop() {
        let data = &def_map[module];
        files.extend(data.origin.file_id());
        modules.extend(data.children.values());
    }

    let snap = Snap(db.snapshot());

    #[cfg(not(feature = "wasm"))]
    let buf = files
        .par_iter()
        .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
        .collect::<Vec<_>>();

    #[cfg(feature = "wasm")]
    let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::<Vec<_>>();

    query.search(&buf)
}

pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
    let name = name_ref.text();
    let mut query = Query::new(name.to_string());
    query.exact();
    query.limit(4);
    world_symbols(db, query)
}

#[derive(Default)]
pub struct SymbolIndex {
    symbols: Vec<FileSymbol>,
    map: fst::Map<Vec<u8>>,
}

impl fmt::Debug for SymbolIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish()
    }
}

impl PartialEq for SymbolIndex {
    fn eq(&self, other: &SymbolIndex) -> bool {
        self.symbols == other.symbols
    }
}

impl Eq for SymbolIndex {}

impl Hash for SymbolIndex {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        self.symbols.hash(hasher)
    }
}

impl SymbolIndex {
    fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
        fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
            let lhs_chars = lhs.name.chars().map(|c| c.to_ascii_lowercase());
            let rhs_chars = rhs.name.chars().map(|c| c.to_ascii_lowercase());
            lhs_chars.cmp(rhs_chars)
        }

        #[cfg(not(feature = "wasm"))]
        symbols.par_sort_by(cmp);

        #[cfg(feature = "wasm")]
        symbols.sort_by(cmp);

        let mut builder = fst::MapBuilder::memory();

        let mut last_batch_start = 0;

        for idx in 0..symbols.len() {
            if let Some(next_symbol) = symbols.get(idx + 1) {
                if cmp(&symbols[last_batch_start], next_symbol) == Ordering::Equal {
                    continue;
                }
            }

            let start = last_batch_start;
            let end = idx + 1;
            last_batch_start = end;

            let key = symbols[start].name.as_str().to_ascii_lowercase();
            let value = SymbolIndex::range_to_map_value(start, end);

            builder.insert(key, value).unwrap();
        }

        let map = fst::Map::new(builder.into_inner().unwrap()).unwrap();
        SymbolIndex { symbols, map }
    }

    pub fn len(&self) -> usize {
        self.symbols.len()
    }

    pub fn memory_size(&self) -> usize {
        self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
    }

    #[cfg(not(feature = "wasm"))]
    pub(crate) fn for_files(
        files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
    ) -> SymbolIndex {
        let symbols = files
            .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
            .collect::<Vec<_>>();
        SymbolIndex::new(symbols)
    }

    #[cfg(feature = "wasm")]
    pub(crate) fn for_files(
        files: impl Iterator<Item = (FileId, Parse<ast::SourceFile>)>,
    ) -> SymbolIndex {
        let symbols = files
            .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
            .collect::<Vec<_>>();
        SymbolIndex::new(symbols)
    }

    fn range_to_map_value(start: usize, end: usize) -> u64 {
        debug_assert![start <= (std::u32::MAX as usize)];
        debug_assert![end <= (std::u32::MAX as usize)];

        ((start as u64) << 32) | end as u64
    }

    fn map_value_to_range(value: u64) -> (usize, usize) {
        let end = value as u32 as usize;
        let start = (value >> 32) as usize;
        (start, end)
    }
}

impl Query {
    pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<FileSymbol> {
        let mut op = fst::map::OpBuilder::new();
        for file_symbols in indices.iter() {
            let automaton = fst::automaton::Subsequence::new(&self.lowercased);
            op = op.add(file_symbols.map.search(automaton))
        }
        let mut stream = op.union();
        let mut res = Vec::new();
        while let Some((_, indexed_values)) = stream.next() {
            for indexed_value in indexed_values {
                let symbol_index = &indices[indexed_value.index];
                let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);

                for symbol in &symbol_index.symbols[start..end] {
                    if self.only_types && !is_type(symbol.kind) {
                        continue;
                    }
                    if self.exact && symbol.name != self.query {
                        continue;
                    }

                    res.push(symbol.clone());
                    if res.len() >= self.limit {
                        return res;
                    }
                }
            }
        }
        res
    }
}

fn is_type(kind: SyntaxKind) -> bool {
    match kind {
        STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => true,
        _ => false,
    }
}

/// The actual data that is stored in the index. It should be as compact as
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol {
    pub file_id: FileId,
    pub name: SmolStr,
    pub kind: SyntaxKind,
    pub range: TextRange,
    pub ptr: SyntaxNodePtr,
    pub name_range: Option<TextRange>,
    pub container_name: Option<SmolStr>,
}

fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec<FileSymbol> {
    let mut symbols = Vec::new();
    let mut stack = Vec::new();

    for event in source_file.syntax().preorder() {
        match event {
            WalkEvent::Enter(node) => {
                if let Some(mut symbol) = to_file_symbol(&node, file_id) {
                    symbol.container_name = stack.last().cloned();

                    stack.push(symbol.name.clone());
                    symbols.push(symbol);
                }
            }

            WalkEvent::Leave(node) => {
                if to_symbol(&node).is_some() {
                    stack.pop();
                }
            }
        }
    }

    symbols
}

fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
    fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
        let name = node.name()?;
        let name_range = name.syntax().text_range();
        let name = name.text().clone();
        let ptr = SyntaxNodePtr::new(node.syntax());

        Some((name, ptr, name_range))
    }
    match_ast! {
        match node {
            ast::FnDef(it) => decl(it),
            ast::StructDef(it) => decl(it),
            ast::EnumDef(it) => decl(it),
            ast::TraitDef(it) => decl(it),
            ast::Module(it) => decl(it),
            ast::TypeAliasDef(it) => decl(it),
            ast::ConstDef(it) => decl(it),
            ast::StaticDef(it) => decl(it),
            ast::MacroCall(it) => {
                if it.is_macro_rules().is_some() {
                    decl(it)
                } else {
                    None
                }
            },
            _ => None,
        }
    }
}

fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
    to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol {
        name,
        kind: node.kind(),
        range: node.text_range(),
        ptr,
        file_id,
        name_range: Some(name_range),
        container_name: None,
    })
}
<|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/. */

//! Generic types that share their serialization implementations
//! for both specified and computed values.

use counter_style::{Symbols, parse_counter_style_name};
use cssparser::Parser;
use euclid::size::Size2D;
use parser::{Parse, ParserContext};
use std::fmt;
use style_traits::{HasViewportPercentage, OneOrMoreCommaSeparated, ToCss};
use super::CustomIdent;

pub use self::basic_shape::serialize_radius_values;

pub mod background;
pub mod basic_shape;
pub mod border;
pub mod grid;
pub mod image;
pub mod position;
pub mod rect;

#[derive(Clone, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A type for representing CSS `width` and `height` values.
pub struct BorderRadiusSize<L>(pub Size2D<L>);

impl<L> HasViewportPercentage for BorderRadiusSize<L> {
    #[inline]
    fn has_viewport_percentage(&self) -> bool { false }
}

impl<L: Clone> From<L> for BorderRadiusSize<L> {
    fn from(other: L) -> Self {
        Self::new(other.clone(), other)
    }
}

impl<L> BorderRadiusSize<L> {
    #[inline]
    /// Create a new `BorderRadiusSize` for an area of given width and height.
    pub fn new(width: L, height: L) -> BorderRadiusSize<L> {
        BorderRadiusSize(Size2D::new(width, height))
    }
}

impl<L: Clone> BorderRadiusSize<L> {
    #[inline]
    /// Create a new `BorderRadiusSize` for a circle of given radius.
    pub fn circle(radius: L) -> BorderRadiusSize<L> {
        BorderRadiusSize(Size2D::new(radius.clone(), radius))
    }
}

impl<L: ToCss> ToCss for BorderRadiusSize<L> {
    #[inline]
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        self.0.width.to_css(dest)?;
        dest.write_str(" ")?;
        self.0.height.to_css(dest)
    }
}

// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
define_css_keyword_enum! { SymbolsType:
    "cyclic" => Cyclic,
    "numeric" => Numeric,
    "alphabetic" => Alphabetic,
    "symbolic" => Symbolic,
    "fixed" => Fixed,
}
add_impls_for_keyword_enum!(SymbolsType);

#[cfg(feature = "gecko")]
impl SymbolsType {
    /// Convert symbols type to their corresponding Gecko values.
    pub fn to_gecko_keyword(self) -> u8 {
        use gecko_bindings::structs;
        match self {
            SymbolsType::Cyclic => structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC as u8,
            SymbolsType::Numeric => structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC as u8,
            SymbolsType::Alphabetic => structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC as u8,
            SymbolsType::Symbolic => structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC as u8,
            SymbolsType::Fixed => structs::NS_STYLE_COUNTER_SYSTEM_FIXED as u8,
        }
    }
}

/// https://drafts.csswg.org/css-counter-styles/#typedef-counter-style
///
/// Since wherever <counter-style> is used, 'none' is a valid value as
/// well, we combine them into one type to make code simpler.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CounterStyleOrNone {
    /// none
    None_,
    /// <counter-style-name>
    Name(CustomIdent),
    /// symbols()
    Symbols(SymbolsType, Symbols),
}

impl CounterStyleOrNone {
    /// disc value
    pub fn disc() -> Self {
        CounterStyleOrNone::Name(CustomIdent(atom!("disc")))
    }

    /// decimal value
    pub fn decimal() -> Self {
        CounterStyleOrNone::Name(CustomIdent(atom!("decimal")))
    }
}

no_viewport_percentage!(CounterStyleOrNone);

impl Parse for CounterStyleOrNone {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(name) = input.try(|i| parse_counter_style_name(i)) {
            return Ok(CounterStyleOrNone::Name(name));
        }
        if input.try(|i| i.expect_ident_matching("none")).is_ok() {
            return Ok(CounterStyleOrNone::None_);
        }
        if input.try(|i| i.expect_function_matching("symbols")).is_ok() {
            return input.parse_nested_block(|input| {
                let symbols_type = input.try(|i| SymbolsType::parse(i))
                    .unwrap_or(SymbolsType::Symbolic);
                let symbols = Symbols::parse(context, input)?;
                // There must be at least two symbols for alphabetic or
                // numeric system.
                if (symbols_type == SymbolsType::Alphabetic ||
                    symbols_type == SymbolsType::Numeric) && symbols.0.len() < 2 {
                    return Err(());
                }
                // Identifier is not allowed in symbols() function.
                if symbols.0.iter().any(|sym| !sym.is_allowed_in_symbols()) {
                    return Err(());
                }
                Ok(CounterStyleOrNone::Symbols(symbols_type, symbols))
            });
        }
        Err(())
    }
}

impl ToCss for CounterStyleOrNone {
    #[inline]
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self {
            &CounterStyleOrNone::None_ => dest.write_str("none"),
            &CounterStyleOrNone::Name(ref name) => name.to_css(dest),
            &CounterStyleOrNone::Symbols(ref symbols_type, ref symbols) => {
                dest.write_str("symbols(")?;
                symbols_type.to_css(dest)?;
                dest.write_str(" ")?;
                symbols.to_css(dest)?;
                dest.write_str(")")
            }
        }
    }
}

/// A settings tag, defined by a four-character tag and a setting value
///
/// For font-feature-settings, this is a tag and an integer,
/// for font-variation-settings this is a tag and a float
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct FontSettingTag<T> {
    /// A four-character tag, packed into a u32 (one byte per character)
    pub tag: u32,
    /// The value
    pub value: T,
}

impl<T> OneOrMoreCommaSeparated for FontSettingTag<T> {}

impl<T: ToCss> ToCss for FontSettingTag<T> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use byteorder::{WriteBytesExt, BigEndian};
        use cssparser::serialize_string;
        use std::str;

        let mut raw: Vec<u8> = vec!();
        raw.write_u32::<BigEndian>(self.tag).unwrap();
        serialize_string(str::from_utf8(&raw).unwrap_or_default(), dest)?;

        self.value.to_css(dest)
    }
}

impl<T: Parse> Parse for FontSettingTag<T> {
    /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
    /// https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-
    /// settings-control-the-font-variation-settings-property
    /// <string> [ on | off | <integer> ]
    /// <string> <number>
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        use byteorder::{ReadBytesExt, BigEndian};
        use std::io::Cursor;

        let tag = try!(input.expect_string());

        // allowed strings of length 4 containing chars: <U+20, U+7E>
        if tag.len() != 4 ||
           tag.chars().any(|c| c < ' ' || c > '~')
        {
            return Err(())
        }

        let mut raw = Cursor::new(tag.as_bytes());
        let u_tag = raw.read_u32::<BigEndian>().unwrap();

        Ok(FontSettingTag { tag: u_tag, value: T::parse(context, input)? })
    }
}

/// A font settings value for font-variation-settings or font-feature-settings
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum FontSettings<T> {
    /// No settings (default)
    Normal,
    /// Set of settings
    Tag(Vec<FontSettingTag<T>>)
}

impl<T: ToCss> ToCss for FontSettings<T> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            FontSettings::Normal => dest.write_str("normal"),
            FontSettings::Tag(ref ftvs) => ftvs.to_css(dest)
        }
    }
}

impl<T: Parse> Parse for FontSettings<T> {
    /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if input.try(|i| i.expect_ident_matching("normal")).is_ok() {
            return Ok(FontSettings::Normal);
        }
        Vec::parse(context, input).map(FontSettings::Tag)
    }
}

/// An integer that can also parse "on" and "off",
/// for font-feature-settings
///
/// Do not use this type anywhere except within FontSettings
/// because it serializes with the preceding space
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct FontSettingTagInt(pub u32);
/// A number value to be used for font-variation-settings
///
/// Do not use this type anywhere except within FontSettings
/// because it serializes with the preceding space
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct FontSettingTagFloat(pub f32);

impl ToCss for FontSettingTagInt {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.0 {
            1 => Ok(()),
            0 => dest.write_str(" off"),
            x => write!(dest, " {}", x)
        }
    }
}

impl Parse for FontSettingTagInt {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(value) = input.try(|input| input.expect_integer()) {
            // handle integer, throw if it is negative
            if value >= 0 {
                Ok(FontSettingTagInt(value as u32))
            } else {
                Err(())
            }
        } else if let Ok(_) = input.try(|input| input.expect_ident_matching("on")) {
            // on is an alias for '1'
            Ok(FontSettingTagInt(1))
        } else if let Ok(_) = input.try(|input| input.expect_ident_matching("off")) {
            // off is an alias for '0'
            Ok(FontSettingTagInt(0))
        } else {
            // empty value is an alias for '1'
            Ok(FontSettingTagInt(1))
        }
    }
}

impl Parse for FontSettingTagFloat {
    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        input.expect_number().map(FontSettingTagFloat)
    }
}

impl ToCss for FontSettingTagFloat {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str(" ")?;
        self.0.to_css(dest)
    }
}

<commit_msg>Using stack-allocated variable for font feature setting<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/. */

//! Generic types that share their serialization implementations
//! for both specified and computed values.

use counter_style::{Symbols, parse_counter_style_name};
use cssparser::Parser;
use euclid::size::Size2D;
use parser::{Parse, ParserContext};
use std::fmt;
use style_traits::{HasViewportPercentage, OneOrMoreCommaSeparated, ToCss};
use super::CustomIdent;

pub use self::basic_shape::serialize_radius_values;

pub mod background;
pub mod basic_shape;
pub mod border;
pub mod grid;
pub mod image;
pub mod position;
pub mod rect;

#[derive(Clone, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A type for representing CSS `width` and `height` values.
pub struct BorderRadiusSize<L>(pub Size2D<L>);

impl<L> HasViewportPercentage for BorderRadiusSize<L> {
    #[inline]
    fn has_viewport_percentage(&self) -> bool { false }
}

impl<L: Clone> From<L> for BorderRadiusSize<L> {
    fn from(other: L) -> Self {
        Self::new(other.clone(), other)
    }
}

impl<L> BorderRadiusSize<L> {
    #[inline]
    /// Create a new `BorderRadiusSize` for an area of given width and height.
    pub fn new(width: L, height: L) -> BorderRadiusSize<L> {
        BorderRadiusSize(Size2D::new(width, height))
    }
}

impl<L: Clone> BorderRadiusSize<L> {
    #[inline]
    /// Create a new `BorderRadiusSize` for a circle of given radius.
    pub fn circle(radius: L) -> BorderRadiusSize<L> {
        BorderRadiusSize(Size2D::new(radius.clone(), radius))
    }
}

impl<L: ToCss> ToCss for BorderRadiusSize<L> {
    #[inline]
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        self.0.width.to_css(dest)?;
        dest.write_str(" ")?;
        self.0.height.to_css(dest)
    }
}

// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
define_css_keyword_enum! { SymbolsType:
    "cyclic" => Cyclic,
    "numeric" => Numeric,
    "alphabetic" => Alphabetic,
    "symbolic" => Symbolic,
    "fixed" => Fixed,
}
add_impls_for_keyword_enum!(SymbolsType);

#[cfg(feature = "gecko")]
impl SymbolsType {
    /// Convert symbols type to their corresponding Gecko values.
    pub fn to_gecko_keyword(self) -> u8 {
        use gecko_bindings::structs;
        match self {
            SymbolsType::Cyclic => structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC as u8,
            SymbolsType::Numeric => structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC as u8,
            SymbolsType::Alphabetic => structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC as u8,
            SymbolsType::Symbolic => structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC as u8,
            SymbolsType::Fixed => structs::NS_STYLE_COUNTER_SYSTEM_FIXED as u8,
        }
    }
}

/// https://drafts.csswg.org/css-counter-styles/#typedef-counter-style
///
/// Since wherever <counter-style> is used, 'none' is a valid value as
/// well, we combine them into one type to make code simpler.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CounterStyleOrNone {
    /// none
    None_,
    /// <counter-style-name>
    Name(CustomIdent),
    /// symbols()
    Symbols(SymbolsType, Symbols),
}

impl CounterStyleOrNone {
    /// disc value
    pub fn disc() -> Self {
        CounterStyleOrNone::Name(CustomIdent(atom!("disc")))
    }

    /// decimal value
    pub fn decimal() -> Self {
        CounterStyleOrNone::Name(CustomIdent(atom!("decimal")))
    }
}

no_viewport_percentage!(CounterStyleOrNone);

impl Parse for CounterStyleOrNone {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(name) = input.try(|i| parse_counter_style_name(i)) {
            return Ok(CounterStyleOrNone::Name(name));
        }
        if input.try(|i| i.expect_ident_matching("none")).is_ok() {
            return Ok(CounterStyleOrNone::None_);
        }
        if input.try(|i| i.expect_function_matching("symbols")).is_ok() {
            return input.parse_nested_block(|input| {
                let symbols_type = input.try(|i| SymbolsType::parse(i))
                    .unwrap_or(SymbolsType::Symbolic);
                let symbols = Symbols::parse(context, input)?;
                // There must be at least two symbols for alphabetic or
                // numeric system.
                if (symbols_type == SymbolsType::Alphabetic ||
                    symbols_type == SymbolsType::Numeric) && symbols.0.len() < 2 {
                    return Err(());
                }
                // Identifier is not allowed in symbols() function.
                if symbols.0.iter().any(|sym| !sym.is_allowed_in_symbols()) {
                    return Err(());
                }
                Ok(CounterStyleOrNone::Symbols(symbols_type, symbols))
            });
        }
        Err(())
    }
}

impl ToCss for CounterStyleOrNone {
    #[inline]
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self {
            &CounterStyleOrNone::None_ => dest.write_str("none"),
            &CounterStyleOrNone::Name(ref name) => name.to_css(dest),
            &CounterStyleOrNone::Symbols(ref symbols_type, ref symbols) => {
                dest.write_str("symbols(")?;
                symbols_type.to_css(dest)?;
                dest.write_str(" ")?;
                symbols.to_css(dest)?;
                dest.write_str(")")
            }
        }
    }
}

/// A settings tag, defined by a four-character tag and a setting value
///
/// For font-feature-settings, this is a tag and an integer,
/// for font-variation-settings this is a tag and a float
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct FontSettingTag<T> {
    /// A four-character tag, packed into a u32 (one byte per character)
    pub tag: u32,
    /// The value
    pub value: T,
}

impl<T> OneOrMoreCommaSeparated for FontSettingTag<T> {}

impl<T: ToCss> ToCss for FontSettingTag<T> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use byteorder::{BigEndian, ByteOrder};
        use cssparser::serialize_string;
        use std::str;

        let mut raw = [0u8; 4];
        BigEndian::write_u32(&mut raw, self.tag);
        serialize_string(str::from_utf8(&raw).unwrap_or_default(), dest)?;

        self.value.to_css(dest)
    }
}

impl<T: Parse> Parse for FontSettingTag<T> {
    /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
    /// https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-
    /// settings-control-the-font-variation-settings-property
    /// <string> [ on | off | <integer> ]
    /// <string> <number>
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        use byteorder::{ReadBytesExt, BigEndian};
        use std::io::Cursor;

        let tag = try!(input.expect_string());

        // allowed strings of length 4 containing chars: <U+20, U+7E>
        if tag.len() != 4 ||
           tag.chars().any(|c| c < ' ' || c > '~')
        {
            return Err(())
        }

        let mut raw = Cursor::new(tag.as_bytes());
        let u_tag = raw.read_u32::<BigEndian>().unwrap();

        Ok(FontSettingTag { tag: u_tag, value: T::parse(context, input)? })
    }
}

/// A font settings value for font-variation-settings or font-feature-settings
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum FontSettings<T> {
    /// No settings (default)
    Normal,
    /// Set of settings
    Tag(Vec<FontSettingTag<T>>)
}

impl<T: ToCss> ToCss for FontSettings<T> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            FontSettings::Normal => dest.write_str("normal"),
            FontSettings::Tag(ref ftvs) => ftvs.to_css(dest)
        }
    }
}

impl<T: Parse> Parse for FontSettings<T> {
    /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if input.try(|i| i.expect_ident_matching("normal")).is_ok() {
            return Ok(FontSettings::Normal);
        }
        Vec::parse(context, input).map(FontSettings::Tag)
    }
}

/// An integer that can also parse "on" and "off",
/// for font-feature-settings
///
/// Do not use this type anywhere except within FontSettings
/// because it serializes with the preceding space
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct FontSettingTagInt(pub u32);
/// A number value to be used for font-variation-settings
///
/// Do not use this type anywhere except within FontSettings
/// because it serializes with the preceding space
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct FontSettingTagFloat(pub f32);

impl ToCss for FontSettingTagInt {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.0 {
            1 => Ok(()),
            0 => dest.write_str(" off"),
            x => write!(dest, " {}", x)
        }
    }
}

impl Parse for FontSettingTagInt {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(value) = input.try(|input| input.expect_integer()) {
            // handle integer, throw if it is negative
            if value >= 0 {
                Ok(FontSettingTagInt(value as u32))
            } else {
                Err(())
            }
        } else if let Ok(_) = input.try(|input| input.expect_ident_matching("on")) {
            // on is an alias for '1'
            Ok(FontSettingTagInt(1))
        } else if let Ok(_) = input.try(|input| input.expect_ident_matching("off")) {
            // off is an alias for '0'
            Ok(FontSettingTagInt(0))
        } else {
            // empty value is an alias for '1'
            Ok(FontSettingTagInt(1))
        }
    }
}

impl Parse for FontSettingTagFloat {
    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        input.expect_number().map(FontSettingTagFloat)
    }
}

impl ToCss for FontSettingTagFloat {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str(" ")?;
        self.0.to_css(dest)
    }
}
<|endoftext|>


<commit_before>/*!
Selector matching

Select matching is performed on generic node types. Client-specific details
about the DOM are encapsulated in the `SelectHandler` type which the `SelectCtx`
uses to query various DOM and UA properties.
*/

use stylesheet::Stylesheet;
use computed::ComputedStyle;
use util::VoidPtrLike;
use wapcaplet::LwcString;
use lwcstr_from_rust_str = wapcaplet::from_rust_string;
use n::u::{rust_str_to_net_qname, net_qname_to_rust_str};
use types::StylesheetOrigin;

pub struct SelectCtx {
    inner: n::s::CssSelectCtx
}

/**
The SelectCtx, used for performing selector matching.

The `SelectCtx` takes ownership of any number of `Stylesheet` objects,
encapsulates the cascade. Individual node styles can be requested with
the `select_style` method.
*/
impl SelectCtx {
    static fn new() -> SelectCtx {
        SelectCtx {
            inner: n::s::css_select_ctx_create()
        }
    }

    /**
    Add `Stylesheet`s to the selection context, where they will participate in the cascade
    during future selector matching
    */
    fn append_sheet(&mut self, sheet: Stylesheet, origin: StylesheetOrigin) {
        let sheet = match move sheet {
            Stylesheet { inner: move inner } => move inner
        };

        self.inner.append_sheet(move sheet, origin.to_net(), n::ll::t::CSS_MEDIA_SCREEN)
    }

    /**
    Select the style for a single node. `handler` is used to query the client for
    a wide range of client-specific details like node relationships, names, and UA
    defaults.
    */
    fn select_style<N: VoidPtrLike, H: SelectHandler<N>>(&self, node: &N, handler: &H) -> SelectResults {
        let inner_handler = SelectHandlerWrapper {
            inner: ptr::to_unsafe_ptr(handler)
        };
        SelectResults {
            inner: self.inner.select_style::<N, SelectHandlerWrapper<N, H>>(node, n::ll::t::CSS_MEDIA_SCREEN, None, &inner_handler)
        }
    }
}

/**
Represents the 'style' of a single node, including it's pseudo-elements.
*/
pub struct SelectResults {
    inner: n::s::CssSelectResults
}

impl SelectResults {
    /** Retrieve the computed style of a single pseudo-element */
    fn computed_style(&self) -> ComputedStyle/&self {
        ComputedStyle {
            inner: self.inner.computed_style(n::s::CssPseudoElementNone)
        }
    }
}

/**
Callbacks used to query the implementation-specific DOM
*/
pub trait SelectHandler<N> {
    fn with_node_name<R>(node: &N, f: &fn(&str) -> R) -> R;
    fn node_id(node: &N) -> Option<~str>;
    fn named_parent_node(node: &N, name: &str) -> Option<N>;
    fn parent_node(node: &N) -> Option<N>;
    fn node_has_id(node: &N, &str) -> bool;
    fn named_ancestor_node(node: &N, name: &str) -> Option<N>;
    fn node_is_root(node: &N) -> bool;
}

/** Used to convert the netsurfcss CssSelectHandler callbacks to out SelectHandler callbacks */
struct SelectHandlerWrapper<N, H: SelectHandler<N>> {
    // FIXME: Can't encode region variables
    inner: *H
}

priv impl<N, H: SelectHandler<N>> SelectHandlerWrapper<N, H> {
    priv fn inner_ref() -> &self/H {
        unsafe { &*self.inner }
    }
}

impl<N, H: SelectHandler<N>> SelectHandlerWrapper<N, H>: n::s::CssSelectHandler<N> {
    fn node_name(node: &N) -> n::t::CssQName {
        do self.inner_ref().with_node_name(node) |name| {
            rust_str_to_net_qname(name)
        }
    }

    fn node_id(node: &N) -> Option<LwcString> {
        self.inner_ref().node_id(node).map(|s| lwcstr_from_rust_str(*s))
    }

    fn named_parent_node(node: &N, qname: &n::t::CssQName) -> Option<N> {
        self.inner_ref().named_parent_node(node, net_qname_to_rust_str(qname))
    }

    fn parent_node(node: &N) -> Option<N> {
        self.inner_ref().parent_node(node)
    }

    fn node_has_id(node: &N, name: LwcString) -> bool {
        self.inner_ref().node_has_id(node, name.to_str_slice())
    }

    fn named_ancestor_node(node: &N, qname: &n::t::CssQName) -> Option<N> {
        self.inner_ref().named_ancestor_node(node, net_qname_to_rust_str(qname))
    }

    fn node_is_root(node: &N) -> bool {
        self.inner_ref().node_is_root(node)
    }

    fn node_is_link(_node: &N) -> bool {
        // FIXME
        warn_unimpl("node_is_link");
        false
    }

    fn ua_default_for_property(property: n::p::CssProperty) -> n::h::CssHint {
        warn!("not specifiying ua default for property %?", property);
        n::h::CssHintDefault
    }
}

fn warn_unimpl(what: &str) {
    warn!("unimplemented select handler: %?", what);
}
<commit_msg>Add with_node_id to eliminate a copy<commit_after>/*!
Selector matching

Select matching is performed on generic node types. Client-specific details
about the DOM are encapsulated in the `SelectHandler` type which the `SelectCtx`
uses to query various DOM and UA properties.
*/

use stylesheet::Stylesheet;
use computed::ComputedStyle;
use util::VoidPtrLike;
use wapcaplet::LwcString;
use lwcstr_from_rust_str = wapcaplet::from_rust_string;
use n::u::{rust_str_to_net_qname, net_qname_to_rust_str};
use types::StylesheetOrigin;

pub struct SelectCtx {
    inner: n::s::CssSelectCtx
}

/**
The SelectCtx, used for performing selector matching.

The `SelectCtx` takes ownership of any number of `Stylesheet` objects,
encapsulates the cascade. Individual node styles can be requested with
the `select_style` method.
*/
impl SelectCtx {
    static fn new() -> SelectCtx {
        SelectCtx {
            inner: n::s::css_select_ctx_create()
        }
    }

    /**
    Add `Stylesheet`s to the selection context, where they will participate in the cascade
    during future selector matching
    */
    fn append_sheet(&mut self, sheet: Stylesheet, origin: StylesheetOrigin) {
        let sheet = match move sheet {
            Stylesheet { inner: move inner } => move inner
        };

        self.inner.append_sheet(move sheet, origin.to_net(), n::ll::t::CSS_MEDIA_SCREEN)
    }

    /**
    Select the style for a single node. `handler` is used to query the client for
    a wide range of client-specific details like node relationships, names, and UA
    defaults.
    */
    fn select_style<N: VoidPtrLike, H: SelectHandler<N>>(&self, node: &N, handler: &H) -> SelectResults {
        let inner_handler = SelectHandlerWrapper {
            inner: ptr::to_unsafe_ptr(handler)
        };
        SelectResults {
            inner: self.inner.select_style::<N, SelectHandlerWrapper<N, H>>(node, n::ll::t::CSS_MEDIA_SCREEN, None, &inner_handler)
        }
    }
}

/**
Represents the 'style' of a single node, including it's pseudo-elements.
*/
pub struct SelectResults {
    inner: n::s::CssSelectResults
}

impl SelectResults {
    /** Retrieve the computed style of a single pseudo-element */
    fn computed_style(&self) -> ComputedStyle/&self {
        ComputedStyle {
            inner: self.inner.computed_style(n::s::CssPseudoElementNone)
        }
    }
}

/**
Callbacks used to query the implementation-specific DOM
*/
pub trait SelectHandler<N> {
    fn with_node_name<R>(node: &N, f: &fn(&str) -> R) -> R;
    fn with_node_id<R>(node: &N, f: &fn(Option<&str>) -> R) -> R;
    fn named_parent_node(node: &N, name: &str) -> Option<N>;
    fn parent_node(node: &N) -> Option<N>;
    fn node_has_id(node: &N, &str) -> bool;
    fn named_ancestor_node(node: &N, name: &str) -> Option<N>;
    fn node_is_root(node: &N) -> bool;
}

/** Used to convert the netsurfcss CssSelectHandler callbacks to out SelectHandler callbacks */
struct SelectHandlerWrapper<N, H: SelectHandler<N>> {
    // FIXME: Can't encode region variables
    inner: *H
}

priv impl<N, H: SelectHandler<N>> SelectHandlerWrapper<N, H> {
    priv fn inner_ref() -> &self/H {
        unsafe { &*self.inner }
    }
}

impl<N, H: SelectHandler<N>> SelectHandlerWrapper<N, H>: n::s::CssSelectHandler<N> {
    fn node_name(node: &N) -> n::t::CssQName {
        do self.inner_ref().with_node_name(node) |name| {
            rust_str_to_net_qname(name)
        }
    }

    fn node_id(node: &N) -> Option<LwcString> {
        do self.inner_ref().with_node_id(node) |node_id_opt| {
            node_id_opt.map(|s| lwcstr_from_rust_str(*s))
        }
    }

    fn named_parent_node(node: &N, qname: &n::t::CssQName) -> Option<N> {
        self.inner_ref().named_parent_node(node, net_qname_to_rust_str(qname))
    }

    fn parent_node(node: &N) -> Option<N> {
        self.inner_ref().parent_node(node)
    }

    fn node_has_id(node: &N, name: LwcString) -> bool {
        self.inner_ref().node_has_id(node, name.to_str_slice())
    }

    fn named_ancestor_node(node: &N, qname: &n::t::CssQName) -> Option<N> {
        self.inner_ref().named_ancestor_node(node, net_qname_to_rust_str(qname))
    }

    fn node_is_root(node: &N) -> bool {
        self.inner_ref().node_is_root(node)
    }

    fn node_is_link(_node: &N) -> bool {
        // FIXME
        warn_unimpl("node_is_link");
        false
    }

    fn ua_default_for_property(property: n::p::CssProperty) -> n::h::CssHint {
        warn!("not specifiying ua default for property %?", property);
        n::h::CssHintDefault
    }
}

fn warn_unimpl(what: &str) {
    warn!("unimplemented select handler: %?", what);
}
<|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/. */

/*!
Selector matching

Select matching is performed on generic node types. Client-specific details
about the DOM are encapsulated in the `SelectHandler` type which the `SelectCtx`
uses to query various DOM and UA properties.
*/

use stylesheet::Stylesheet;
use computed::ComputedStyle;
use util::VoidPtrLike;
use wapcaplet::LwcString;
use lwcstr_from_rust_str = wapcaplet::from_rust_string;
use n::u::{rust_str_to_net_qname, net_qname_to_rust_str};
use types::StylesheetOrigin;
use n;

pub struct SelectCtx {
    inner: n::s::CssSelectCtx
}

/**
The SelectCtx, used for performing selector matching.

The `SelectCtx` takes ownership of any number of `Stylesheet` objects,
encapsulates the cascade. Individual node styles can be requested with
the `select_style` method.
*/
pub impl SelectCtx {
    fn new() -> SelectCtx {
        SelectCtx {
            inner: n::s::css_select_ctx_create()
        }
    }

    /**
    Add `Stylesheet`s to the selection context, where they will participate in the cascade
    during future selector matching
    */
    fn append_sheet(&mut self, sheet: Stylesheet, origin: StylesheetOrigin) {
        let sheet = match sheet {
            Stylesheet { inner: inner } => inner
        };

        self.inner.append_sheet(sheet, origin.to_net(), n::ll::t::CSS_MEDIA_SCREEN)
    }

    /**
    Select the style for a single node. `handler` is used to query the client for
    a wide range of client-specific details like node relationships, names, and UA
    defaults.
    */
    fn select_style<N: VoidPtrLike, H: SelectHandler<N>>(&self, node: &N, handler: &H) -> SelectResults {
        let inner_handler = SelectHandlerWrapper {
            inner: ptr::to_unsafe_ptr(handler)
        };
        SelectResults {
            inner: self.inner.select_style::<N, SelectHandlerWrapper<N, H>>(node, n::ll::t::CSS_MEDIA_SCREEN, None, &inner_handler)
        }
    }
}

/**
Represents the 'style' of a single node, including it's pseudo-elements.
*/
pub struct SelectResults {
    inner: n::s::CssSelectResults
}

pub impl<'self> SelectResults {
    /** Retrieve the computed style of a single pseudo-element */
    fn computed_style(&'self self) -> ComputedStyle<'self> {
        ComputedStyle {
            inner: self.inner.computed_style(n::s::CssPseudoElementNone)
        }
    }
}

/**
Callbacks used to query the implementation-specific DOM
*/
pub trait SelectHandler<N> {
    fn with_node_name<R>(&self, node: &N, f: &fn(&str) -> R) -> R;
    fn with_node_classes<R>(&self, node: &N, f: &fn(Option<&str>) -> R) -> R;
    fn with_node_id<R>(&self, node: &N, f: &fn(Option<&str>) -> R) -> R;
    fn named_parent_node(&self, node: &N, name: &str) -> Option<N>;
    fn parent_node(&self, node: &N) -> Option<N>;
    fn node_has_id(&self, node: &N, &str) -> bool;
    fn named_ancestor_node(&self, node: &N, name: &str) -> Option<N>;
    fn node_is_root(&self, node: &N) -> bool;
}

/** Used to convert the netsurfcss CssSelectHandler callbacks to out SelectHandler callbacks */
struct SelectHandlerWrapper<N, H> {
    // FIXME: Can't encode region variables
    inner: *H
}

priv impl<'self, N, H: SelectHandler<N>> SelectHandlerWrapper<N, H> {
    priv fn inner_ref(&self) -> &'self H {
        unsafe { &*self.inner }
    }
}

impl<N, H: SelectHandler<N>> n::s::CssSelectHandler<N> for SelectHandlerWrapper<N, H> {
    fn node_name(&self, node: &N) -> n::t::CssQName {
        do self.inner_ref().with_node_name(node) |name| {
            rust_str_to_net_qname(name)
        }
    }

    fn node_classes(&self, node: &N) -> Option<~[LwcString]> {
        do self.inner_ref().with_node_classes(node) |node_classes_opt| {
           do node_classes_opt.map |s| {
               debug!("SelectHandlerWrapper::node_classes - classes: %?", *s);
               let mut v = ~[];
               for str::each_split_char(*s, ' ') |s| {
                   debug!("SelectHandlerWrapper::node_classes - class: %?", s);
                   if s != ~"" { v.push(lwcstr_from_rust_str(s)) }
               }
               debug!("SelectHandlerWrapper::classes: %?", v);
               v
           }
        }
    }

    fn node_id(&self, node: &N) -> Option<LwcString> {
        do self.inner_ref().with_node_id(node) |node_id_opt| {
            node_id_opt.map(|s| lwcstr_from_rust_str(*s))
        }
    }

    fn named_parent_node(&self, node: &N, qname: &n::t::CssQName) -> Option<N> {
        self.inner_ref().named_parent_node(node, net_qname_to_rust_str(qname))
    }

    fn parent_node(&self, node: &N) -> Option<N> {
        self.inner_ref().parent_node(node)
    }

    fn node_has_id(&self, node: &N, name: LwcString) -> bool {
        self.inner_ref().node_has_id(node, name.to_str_slice())
    }

    fn named_ancestor_node(&self, node: &N, qname: &n::t::CssQName) -> Option<N> {
        self.inner_ref().named_ancestor_node(node, net_qname_to_rust_str(qname))
    }

    fn node_is_root(&self, node: &N) -> bool {
        self.inner_ref().node_is_root(node)
    }

    fn node_is_link(&self, _node: &N) -> bool {
        // FIXME
        warn_unimpl("node_is_link");
        false
    }

    fn node_is_visited(&self, _node: &N) -> bool {
        // FIXME
        warn_unimpl("node_is_visited");
        false
    }

    fn ua_default_for_property(&self, property: n::p::CssProperty) -> n::h::CssHint {
        warn!("not specifiying ua default for property %?", property);
        n::h::CssHintDefault
    }
}

fn warn_unimpl(what: &str) {
    warn!("unimplemented select handler: %?", what);
}
<commit_msg>Support css class selection (2)<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/. */

/*!
Selector matching

Select matching is performed on generic node types. Client-specific details
about the DOM are encapsulated in the `SelectHandler` type which the `SelectCtx`
uses to query various DOM and UA properties.
*/

use stylesheet::Stylesheet;
use computed::ComputedStyle;
use util::VoidPtrLike;
use wapcaplet::LwcString;
use lwcstr_from_rust_str = wapcaplet::from_rust_string;
use n::u::{rust_str_to_net_qname, net_qname_to_rust_str};
use types::StylesheetOrigin;
use n;

pub struct SelectCtx {
    inner: n::s::CssSelectCtx
}

/**
The SelectCtx, used for performing selector matching.

The `SelectCtx` takes ownership of any number of `Stylesheet` objects,
encapsulates the cascade. Individual node styles can be requested with
the `select_style` method.
*/
pub impl SelectCtx {
    fn new() -> SelectCtx {
        SelectCtx {
            inner: n::s::css_select_ctx_create()
        }
    }

    /**
    Add `Stylesheet`s to the selection context, where they will participate in the cascade
    during future selector matching
    */
    fn append_sheet(&mut self, sheet: Stylesheet, origin: StylesheetOrigin) {
        let sheet = match sheet {
            Stylesheet { inner: inner } => inner
        };

        self.inner.append_sheet(sheet, origin.to_net(), n::ll::t::CSS_MEDIA_SCREEN)
    }

    /**
    Select the style for a single node. `handler` is used to query the client for
    a wide range of client-specific details like node relationships, names, and UA
    defaults.
    */
    fn select_style<N: VoidPtrLike, H: SelectHandler<N>>(&self, node: &N, handler: &H) -> SelectResults {
        let inner_handler = SelectHandlerWrapper {
            inner: ptr::to_unsafe_ptr(handler)
        };
        SelectResults {
            inner: self.inner.select_style::<N, SelectHandlerWrapper<N, H>>(node, n::ll::t::CSS_MEDIA_SCREEN, None, &inner_handler)
        }
    }
}

/**
Represents the 'style' of a single node, including it's pseudo-elements.
*/
pub struct SelectResults {
    inner: n::s::CssSelectResults
}

pub impl<'self> SelectResults {
    /** Retrieve the computed style of a single pseudo-element */
    fn computed_style(&'self self) -> ComputedStyle<'self> {
        ComputedStyle {
            inner: self.inner.computed_style(n::s::CssPseudoElementNone)
        }
    }
}

/**
Callbacks used to query the implementation-specific DOM
*/
pub trait SelectHandler<N> {
    fn with_node_name<R>(&self, node: &N, f: &fn(&str) -> R) -> R;
    fn with_node_classes<R>(&self, node: &N, f: &fn(Option<&str>) -> R) -> R;
    fn with_node_id<R>(&self, node: &N, f: &fn(Option<&str>) -> R) -> R;
    fn named_parent_node(&self, node: &N, name: &str) -> Option<N>;
    fn parent_node(&self, node: &N) -> Option<N>;
    fn node_has_class(&self, node: &N, &str) -> bool;
    fn node_has_id(&self, node: &N, &str) -> bool;
    fn named_ancestor_node(&self, node: &N, name: &str) -> Option<N>;
    fn node_is_root(&self, node: &N) -> bool;
}

/** Used to convert the netsurfcss CssSelectHandler callbacks to out SelectHandler callbacks */
struct SelectHandlerWrapper<N, H> {
    // FIXME: Can't encode region variables
    inner: *H
}

priv impl<'self, N, H: SelectHandler<N>> SelectHandlerWrapper<N, H> {
    priv fn inner_ref(&self) -> &'self H {
        unsafe { &*self.inner }
    }
}

impl<N, H: SelectHandler<N>> n::s::CssSelectHandler<N> for SelectHandlerWrapper<N, H> {
    fn node_name(&self, node: &N) -> n::t::CssQName {
        do self.inner_ref().with_node_name(node) |name| {
            rust_str_to_net_qname(name)
        }
    }

    fn node_classes(&self, node: &N) -> Option<~[LwcString]> {
        do self.inner_ref().with_node_classes(node) |node_classes_opt| {
           do node_classes_opt.map |s| {
               debug!("SelectHandlerWrapper::node_classes - classes: %?", *s);
               let mut v = ~[];
               for str::each_split_char(*s, ' ') |s| {
                   debug!("SelectHandlerWrapper::node_classes - class: %?", s);
                   if s != ~"" { v.push(lwcstr_from_rust_str(s)) }
               }
               debug!("SelectHandlerWrapper::classes: %?", v);
               v
           }
        }
    }

    fn node_id(&self, node: &N) -> Option<LwcString> {
        do self.inner_ref().with_node_id(node) |node_id_opt| {
            node_id_opt.map(|s| lwcstr_from_rust_str(*s))
        }
    }

    fn named_parent_node(&self, node: &N, qname: &n::t::CssQName) -> Option<N> {
        self.inner_ref().named_parent_node(node, net_qname_to_rust_str(qname))
    }

    fn parent_node(&self, node: &N) -> Option<N> {
        self.inner_ref().parent_node(node)
    }

    fn node_has_class(&self, node: &N, name: LwcString) -> bool {
        self.inner_ref().node_has_class(node, name.to_str_slice())
    }

    fn node_has_id(&self, node: &N, name: LwcString) -> bool {
        self.inner_ref().node_has_id(node, name.to_str_slice())
    }

    fn named_ancestor_node(&self, node: &N, qname: &n::t::CssQName) -> Option<N> {
        self.inner_ref().named_ancestor_node(node, net_qname_to_rust_str(qname))
    }

    fn node_is_root(&self, node: &N) -> bool {
        self.inner_ref().node_is_root(node)
    }

    fn node_is_link(&self, _node: &N) -> bool {
        // FIXME
        warn_unimpl("node_is_link");
        false
    }

    fn node_is_visited(&self, _node: &N) -> bool {
        // FIXME
        warn_unimpl("node_is_visited");
        false
    }

    fn ua_default_for_property(&self, property: n::p::CssProperty) -> n::h::CssHint {
        warn!("not specifiying ua default for property %?", property);
        n::h::CssHintDefault
    }
}

fn warn_unimpl(what: &str) {
    warn!("unimplemented select handler: %?", what);
}
<|endoftext|>