<commit_before>use std::{
    error::Error,
    io::{Read, Write},
    process::{Command, Stdio},
    sync::Arc,
};

use dot::Id;
use ide_db::{
    base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
    RootDatabase,
};
use rustc_hash::FxHashSet;

// Feature: View Crate Graph
//
// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool, which
// is part of graphviz, to be installed.
//
// Only workspace crates are included, no crates.io dependencies or sysroot crates.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: View Crate Graph**
// |===
pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
    let crate_graph = db.crate_graph();
    let crates_to_render = crate_graph
        .iter()
        .filter(|krate| {
            // Only render workspace crates
            let root_id = db.file_source_root(crate_graph[*krate].root_file_id);
            !db.source_root(root_id).is_library
        })
        .collect();
    let graph = DotCrateGraph { graph: crate_graph, crates_to_render };

    let mut dot = Vec::new();
    dot::render(&graph, &mut dot).unwrap();

    render_svg(&dot).map_err(|e| e.to_string())
}

fn render_svg(dot: &[u8]) -> Result<String, Box<dyn Error>> {
    // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
    let child = Command::new("dot")
        .arg("-Tsvg")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .map_err(|err| format!("failed to spawn `dot`: {}", err))?;
    child.stdin.unwrap().write_all(&dot)?;

    let mut svg = String::new();
    child.stdout.unwrap().read_to_string(&mut svg)?;
    Ok(svg)
}

struct DotCrateGraph {
    graph: Arc<CrateGraph>,
    crates_to_render: FxHashSet<CrateId>,
}

type Edge<'a> = (CrateId, &'a Dependency);

impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
    fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
        self.crates_to_render.iter().copied().collect()
    }

    fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
        self.crates_to_render
            .iter()
            .flat_map(|krate| {
                self.graph[*krate]
                    .dependencies
                    .iter()
                    .filter(|dep| self.crates_to_render.contains(&dep.crate_id))
                    .map(move |dep| (*krate, dep))
            })
            .collect()
    }

    fn source(&'a self, edge: &Edge<'a>) -> CrateId {
        edge.0
    }

    fn target(&'a self, edge: &Edge<'a>) -> CrateId {
        edge.1.crate_id
    }
}

impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
    fn graph_id(&'a self) -> Id<'a> {
        Id::new("rust_analyzer_crate_graph").unwrap()
    }

    fn node_id(&'a self, n: &CrateId) -> Id<'a> {
        let name = self.graph[*n].display_name.as_ref().map_or("_missing_name_", |name| &*name);
        Id::new(format!("{}_{}", name, n.0)).unwrap()
    }
}
<commit_msg>Better node label/shapes<commit_after>use std::{
    error::Error,
    io::{Read, Write},
    process::{Command, Stdio},
    sync::Arc,
};

use dot::{Id, LabelText};
use ide_db::{
    base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
    RootDatabase,
};
use rustc_hash::FxHashSet;

// Feature: View Crate Graph
//
// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool, which
// is part of graphviz, to be installed.
//
// Only workspace crates are included, no crates.io dependencies or sysroot crates.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: View Crate Graph**
// |===
pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
    let crate_graph = db.crate_graph();
    let crates_to_render = crate_graph
        .iter()
        .filter(|krate| {
            // Only render workspace crates
            let root_id = db.file_source_root(crate_graph[*krate].root_file_id);
            !db.source_root(root_id).is_library
        })
        .collect();
    let graph = DotCrateGraph { graph: crate_graph, crates_to_render };

    let mut dot = Vec::new();
    dot::render(&graph, &mut dot).unwrap();

    render_svg(&dot).map_err(|e| e.to_string())
}

fn render_svg(dot: &[u8]) -> Result<String, Box<dyn Error>> {
    // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
    let child = Command::new("dot")
        .arg("-Tsvg")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .map_err(|err| format!("failed to spawn `dot`: {}", err))?;
    child.stdin.unwrap().write_all(&dot)?;

    let mut svg = String::new();
    child.stdout.unwrap().read_to_string(&mut svg)?;
    Ok(svg)
}

struct DotCrateGraph {
    graph: Arc<CrateGraph>,
    crates_to_render: FxHashSet<CrateId>,
}

type Edge<'a> = (CrateId, &'a Dependency);

impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
    fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
        self.crates_to_render.iter().copied().collect()
    }

    fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
        self.crates_to_render
            .iter()
            .flat_map(|krate| {
                self.graph[*krate]
                    .dependencies
                    .iter()
                    .filter(|dep| self.crates_to_render.contains(&dep.crate_id))
                    .map(move |dep| (*krate, dep))
            })
            .collect()
    }

    fn source(&'a self, edge: &Edge<'a>) -> CrateId {
        edge.0
    }

    fn target(&'a self, edge: &Edge<'a>) -> CrateId {
        edge.1.crate_id
    }
}

impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
    fn graph_id(&'a self) -> Id<'a> {
        Id::new("rust_analyzer_crate_graph").unwrap()
    }

    fn node_id(&'a self, n: &CrateId) -> Id<'a> {
        Id::new(format!("_{}", n.0)).unwrap()
    }

    fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> {
        Some(LabelText::LabelStr("box".into()))
    }

    fn node_label(&'a self, n: &CrateId) -> LabelText<'a> {
        let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| &*name);
        LabelText::LabelStr(name.into())
    }
}
<|endoftext|>


<commit_before>use ra_syntax::{
    AstNode, SyntaxNode, TextRange,
    ast, SyntaxKind::*, TextUnit
};

/// Maps `tt::TokenId` to the relative range of the original token.
#[derive(Default)]
pub struct TokenMap {
    /// Maps `tt::TokenId` to the *relative* source range.
    toknes: Vec<TextRange>,
}

/// Convert the syntax tree (what user has written) to a `TokenTree` (what macro
/// will consume).
pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> {
    let mut token_map = TokenMap::default();
    let node = ast.syntax();
    let tt = convert_tt(&mut token_map, node.range().start(), node)?;
    Some((tt, token_map))
}

/// Parses the token tree (result of macro expansion) as a sequence of items
pub fn token_tree_to_ast_item_list(tt: &tt::Subtree) -> ast::SourceFile {
    unimplemented!()
}

impl TokenMap {
    pub fn relative_range_of(&self, tt: tt::TokenId) -> Option<TextRange> {
        let idx = tt.0 as usize;
        self.toknes.get(idx).map(|&it| it)
    }

    fn alloc(&mut self, relative_range: TextRange) -> tt::TokenId {
        let id = self.toknes.len();
        self.toknes.push(relative_range);
        tt::TokenId(id as u32)
    }
}

fn convert_tt(
    token_map: &mut TokenMap,
    global_offset: TextUnit,
    tt: &SyntaxNode,
) -> Option<tt::Subtree> {
    let first_child = tt.first_child()?;
    let last_child = tt.last_child()?;
    let delimiter = match (first_child.kind(), last_child.kind()) {
        (L_PAREN, R_PAREN) => tt::Delimiter::Parenthesis,
        (L_CURLY, R_CURLY) => tt::Delimiter::Brace,
        (L_BRACK, R_BRACK) => tt::Delimiter::Bracket,
        _ => return None,
    };
    let mut token_trees = Vec::new();
    for child in tt.children().skip(1) {
        if child == first_child || child == last_child || child.kind().is_trivia() {
            continue;
        }
        if child.kind().is_punct() {
            let mut prev = None;
            for char in child.leaf_text().unwrap().chars() {
                if let Some(char) = prev {
                    token_trees.push(
                        tt::Leaf::from(tt::Punct { char, spacing: tt::Spacing::Joint }).into(),
                    );
                }
                prev = Some(char)
            }
            if let Some(char) = prev {
                token_trees
                    .push(tt::Leaf::from(tt::Punct { char, spacing: tt::Spacing::Alone }).into());
            }
        } else {
            let child: tt::TokenTree = if child.kind() == TOKEN_TREE {
                convert_tt(token_map, global_offset, child)?.into()
            } else if child.kind().is_keyword() || child.kind() == IDENT {
                let relative_range = child.range() - global_offset;
                let id = token_map.alloc(relative_range);
                let text = child.leaf_text().unwrap().clone();
                tt::Leaf::from(tt::Ident { text, id }).into()
            } else if child.kind().is_literal() {
                tt::Leaf::from(tt::Literal { text: child.leaf_text().unwrap().clone() }).into()
            } else {
                return None;
            };
            token_trees.push(child)
        }
    }

    let res = tt::Subtree { delimiter, token_trees };
    Some(res)
}
<commit_msg>token source scaffold<commit_after>use ra_parser::TokenSource;
use ra_syntax::{
    AstNode, SyntaxNode, TextRange, SyntaxKind,
    ast, SyntaxKind::*, TextUnit
};

/// Maps `tt::TokenId` to the relative range of the original token.
#[derive(Default)]
pub struct TokenMap {
    /// Maps `tt::TokenId` to the *relative* source range.
    toknes: Vec<TextRange>,
}

/// Convert the syntax tree (what user has written) to a `TokenTree` (what macro
/// will consume).
pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> {
    let mut token_map = TokenMap::default();
    let node = ast.syntax();
    let tt = convert_tt(&mut token_map, node.range().start(), node)?;
    Some((tt, token_map))
}

/// Parses the token tree (result of macro expansion) as a sequence of items
pub fn token_tree_to_ast_item_list(tt: &tt::Subtree) -> ast::SourceFile {
    unimplemented!()
}

impl TokenMap {
    pub fn relative_range_of(&self, tt: tt::TokenId) -> Option<TextRange> {
        let idx = tt.0 as usize;
        self.toknes.get(idx).map(|&it| it)
    }

    fn alloc(&mut self, relative_range: TextRange) -> tt::TokenId {
        let id = self.toknes.len();
        self.toknes.push(relative_range);
        tt::TokenId(id as u32)
    }
}

fn convert_tt(
    token_map: &mut TokenMap,
    global_offset: TextUnit,
    tt: &SyntaxNode,
) -> Option<tt::Subtree> {
    let first_child = tt.first_child()?;
    let last_child = tt.last_child()?;
    let delimiter = match (first_child.kind(), last_child.kind()) {
        (L_PAREN, R_PAREN) => tt::Delimiter::Parenthesis,
        (L_CURLY, R_CURLY) => tt::Delimiter::Brace,
        (L_BRACK, R_BRACK) => tt::Delimiter::Bracket,
        _ => return None,
    };
    let mut token_trees = Vec::new();
    for child in tt.children().skip(1) {
        if child == first_child || child == last_child || child.kind().is_trivia() {
            continue;
        }
        if child.kind().is_punct() {
            let mut prev = None;
            for char in child.leaf_text().unwrap().chars() {
                if let Some(char) = prev {
                    token_trees.push(
                        tt::Leaf::from(tt::Punct { char, spacing: tt::Spacing::Joint }).into(),
                    );
                }
                prev = Some(char)
            }
            if let Some(char) = prev {
                token_trees
                    .push(tt::Leaf::from(tt::Punct { char, spacing: tt::Spacing::Alone }).into());
            }
        } else {
            let child: tt::TokenTree = if child.kind() == TOKEN_TREE {
                convert_tt(token_map, global_offset, child)?.into()
            } else if child.kind().is_keyword() || child.kind() == IDENT {
                let relative_range = child.range() - global_offset;
                let id = token_map.alloc(relative_range);
                let text = child.leaf_text().unwrap().clone();
                tt::Leaf::from(tt::Ident { text, id }).into()
            } else if child.kind().is_literal() {
                tt::Leaf::from(tt::Literal { text: child.leaf_text().unwrap().clone() }).into()
            } else {
                return None;
            };
            token_trees.push(child)
        }
    }

    let res = tt::Subtree { delimiter, token_trees };
    Some(res)
}

struct TtTokenSource;

impl TtTokenSource {
    fn new(tt: &tt::Subtree) -> TtTokenSource {
        unimplemented!()
    }
}

impl TokenSource for TtTokenSource {
    fn token_kind(&self, pos: usize) -> SyntaxKind {
        unimplemented!()
    }
    fn is_token_joint_to_next(&self, pos: usize) -> bool {
        unimplemented!()
    }
    fn is_keyword(&self, pos: usize, kw: &str) -> bool {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>//! Config used by the language server.
//!
//! We currently get this config from `initialize` LSP request, which is not the
//! best way to do it, but was the simplest thing we could implement.
//!
//! Of particular interest is the `feature_flags` hash map: while other fields
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.

use lsp_types::TextDocumentClientCapabilities;
use ra_flycheck::FlycheckConfig;
use ra_ide::{CompletionConfig, InlayHintsConfig};
use ra_project_model::CargoConfig;
use serde::Deserialize;

#[derive(Debug, Clone)]
pub struct Config {
    pub client_caps: ClientCapsConfig,

    pub with_sysroot: bool,
    pub publish_diagnostics: bool,
    pub lru_capacity: Option<usize>,
    pub proc_macro_srv: Option<(String, Vec<String>)>,
    pub files: FilesConfig,
    pub notifications: NotificationsConfig,

    pub cargo: CargoConfig,
    pub rustfmt: RustfmtConfig,
    pub check: Option<FlycheckConfig>,

    pub inlay_hints: InlayHintsConfig,
    pub completion: CompletionConfig,
    pub call_info_full: bool,
}

#[derive(Debug, Clone)]
pub struct FilesConfig {
    pub watcher: FilesWatcher,
    pub exclude: Vec<String>,
}

#[derive(Debug, Clone)]
pub enum FilesWatcher {
    Client,
    Notify,
}

#[derive(Debug, Clone)]
pub struct NotificationsConfig {
    pub workspace_loaded: bool,
    pub cargo_toml_not_found: bool,
}

#[derive(Debug, Clone)]
pub enum RustfmtConfig {
    Rustfmt {
        extra_args: Vec<String>,
    },
    #[allow(unused)]
    CustomCommand {
        command: String,
        args: Vec<String>,
    },
}

#[derive(Debug, Clone, Default)]
pub struct ClientCapsConfig {
    pub location_link: bool,
    pub line_folding_only: bool,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            client_caps: ClientCapsConfig::default(),

            with_sysroot: true,
            publish_diagnostics: true,
            lru_capacity: None,
            proc_macro_srv: None,
            files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
            notifications: NotificationsConfig {
                workspace_loaded: true,
                cargo_toml_not_found: true,
            },

            cargo: CargoConfig::default(),
            rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
            check: Some(FlycheckConfig::CargoCommand {
                command: "check".to_string(),
                all_targets: true,
                extra_args: Vec::new(),
            }),

            inlay_hints: InlayHintsConfig {
                type_hints: true,
                parameter_hints: true,
                chaining_hints: true,
                max_length: None,
            },
            completion: CompletionConfig {
                enable_postfix_completions: true,
                add_call_parenthesis: true,
                add_call_argument_snippets: true,
            },
            call_info_full: true,
        }
    }
}

impl Config {
    #[rustfmt::skip]
    pub fn update(&mut self, value: &serde_json::Value) {
        log::info!("Config::update({:#})", value);

        let client_caps = self.client_caps.clone();
        *self = Default::default();
        self.client_caps = client_caps;

        set(value, "/withSysroot", &mut self.with_sysroot);
        set(value, "/diagnostics/enable", &mut self.publish_diagnostics);
        set(value, "/lruCapacity", &mut self.lru_capacity);
        self.files.watcher = match get(value, "/files/watcher") {
            Some("client") => FilesWatcher::Client,
            Some("notify") | _ => FilesWatcher::Notify
        };
        set(value, "/notifications/workspaceLoaded", &mut self.notifications.workspace_loaded);
        set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);

        set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
        set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
        set(value, "/cargo/features", &mut self.cargo.features);
        set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);

        match get::<bool>(value, "/procMacro/enable") {
            Some(true) => {
                if let Ok(path) = std::env::current_exe() {
                    self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()]));
                }
            }
            _ => self.proc_macro_srv = None,
        }

        match get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
            Some(mut args) if !args.is_empty() => {
                let command = args.remove(0);
                self.rustfmt = RustfmtConfig::CustomCommand {
                    command,
                    args,
                }
            }
            _ => {
                if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
                    set(value, "/rustfmt/extraArgs", extra_args);
                }
            }
        };

        if let Some(false) = get(value, "/checkOnSave/enable") {
            // check is disabled
            self.check = None;
        } else {
            // check is enabled
            match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
                // first see if the user has completely overridden the command
                Some(mut args) if !args.is_empty() => {
                    let command = args.remove(0);
                    self.check = Some(FlycheckConfig::CustomCommand {
                        command,
                        args,
                    });
                }
                // otherwise configure command customizations
                _ => {
                    if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets })
                        = &mut self.check
                    {
                        set(value, "/checkOnSave/extraArgs", extra_args);
                        set(value, "/checkOnSave/command", command);
                        set(value, "/checkOnSave/allTargets", all_targets);
                    }
                }
            };
        }

        set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints);
        set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints);
        set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints);
        set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length);
        set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions);
        set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis);
        set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
        set(value, "/callInfo/full", &mut self.call_info_full);

        log::info!("Config::update() = {:#?}", self);

        fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
            value.pointer(pointer).and_then(|it| T::deserialize(it).ok())
        }

        fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) {
            if let Some(new_value) = get(value, pointer) {
                *slot = new_value
            }
        }
    }

    pub fn update_caps(&mut self, caps: &TextDocumentClientCapabilities) {
        if let Some(value) = caps.definition.as_ref().and_then(|it| it.link_support) {
            self.client_caps.location_link = value;
        }
        if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
            self.client_caps.line_folding_only = value
        }
    }
}
<commit_msg>Simplify config<commit_after>//! Config used by the language server.
//!
//! We currently get this config from `initialize` LSP request, which is not the
//! best way to do it, but was the simplest thing we could implement.
//!
//! Of particular interest is the `feature_flags` hash map: while other fields
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.

use lsp_types::TextDocumentClientCapabilities;
use ra_flycheck::FlycheckConfig;
use ra_ide::{CompletionConfig, InlayHintsConfig};
use ra_project_model::CargoConfig;
use serde::Deserialize;

#[derive(Debug, Clone)]
pub struct Config {
    pub client_caps: ClientCapsConfig,

    pub with_sysroot: bool,
    pub publish_diagnostics: bool,
    pub lru_capacity: Option<usize>,
    pub proc_macro_srv: Option<(String, Vec<String>)>,
    pub files: FilesConfig,
    pub notifications: NotificationsConfig,

    pub cargo: CargoConfig,
    pub rustfmt: RustfmtConfig,
    pub check: Option<FlycheckConfig>,

    pub inlay_hints: InlayHintsConfig,
    pub completion: CompletionConfig,
    pub call_info_full: bool,
}

#[derive(Debug, Clone)]
pub struct FilesConfig {
    pub watcher: FilesWatcher,
    pub exclude: Vec<String>,
}

#[derive(Debug, Clone)]
pub enum FilesWatcher {
    Client,
    Notify,
}

#[derive(Debug, Clone)]
pub struct NotificationsConfig {
    pub workspace_loaded: bool,
    pub cargo_toml_not_found: bool,
}

#[derive(Debug, Clone)]
pub enum RustfmtConfig {
    Rustfmt {
        extra_args: Vec<String>,
    },
    #[allow(unused)]
    CustomCommand {
        command: String,
        args: Vec<String>,
    },
}

#[derive(Debug, Clone, Default)]
pub struct ClientCapsConfig {
    pub location_link: bool,
    pub line_folding_only: bool,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            client_caps: ClientCapsConfig::default(),

            with_sysroot: true,
            publish_diagnostics: true,
            lru_capacity: None,
            proc_macro_srv: None,
            files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
            notifications: NotificationsConfig {
                workspace_loaded: true,
                cargo_toml_not_found: true,
            },

            cargo: CargoConfig::default(),
            rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
            check: Some(FlycheckConfig::CargoCommand {
                command: "check".to_string(),
                all_targets: true,
                extra_args: Vec::new(),
            }),

            inlay_hints: InlayHintsConfig {
                type_hints: true,
                parameter_hints: true,
                chaining_hints: true,
                max_length: None,
            },
            completion: CompletionConfig {
                enable_postfix_completions: true,
                add_call_parenthesis: true,
                add_call_argument_snippets: true,
            },
            call_info_full: true,
        }
    }
}

impl Config {
    #[rustfmt::skip]
    pub fn update(&mut self, value: &serde_json::Value) {
        log::info!("Config::update({:#})", value);

        let client_caps = self.client_caps.clone();
        *self = Default::default();
        self.client_caps = client_caps;

        set(value, "/withSysroot", &mut self.with_sysroot);
        set(value, "/diagnostics/enable", &mut self.publish_diagnostics);
        set(value, "/lruCapacity", &mut self.lru_capacity);
        self.files.watcher = match get(value, "/files/watcher") {
            Some("client") => FilesWatcher::Client,
            Some("notify") | _ => FilesWatcher::Notify
        };
        set(value, "/notifications/workspaceLoaded", &mut self.notifications.workspace_loaded);
        set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);

        set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
        set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
        set(value, "/cargo/features", &mut self.cargo.features);
        set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);

        match get(value, "/procMacro/enable") {
            Some(true) => {
                if let Ok(path) = std::env::current_exe() {
                    self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()]));
                }
            }
            _ => self.proc_macro_srv = None,
        }

        match get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
            Some(mut args) if !args.is_empty() => {
                let command = args.remove(0);
                self.rustfmt = RustfmtConfig::CustomCommand {
                    command,
                    args,
                }
            }
            _ => {
                if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
                    set(value, "/rustfmt/extraArgs", extra_args);
                }
            }
        };

        if let Some(false) = get(value, "/checkOnSave/enable") {
            // check is disabled
            self.check = None;
        } else {
            // check is enabled
            match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
                // first see if the user has completely overridden the command
                Some(mut args) if !args.is_empty() => {
                    let command = args.remove(0);
                    self.check = Some(FlycheckConfig::CustomCommand {
                        command,
                        args,
                    });
                }
                // otherwise configure command customizations
                _ => {
                    if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets })
                        = &mut self.check
                    {
                        set(value, "/checkOnSave/extraArgs", extra_args);
                        set(value, "/checkOnSave/command", command);
                        set(value, "/checkOnSave/allTargets", all_targets);
                    }
                }
            };
        }

        set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints);
        set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints);
        set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints);
        set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length);
        set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions);
        set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis);
        set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
        set(value, "/callInfo/full", &mut self.call_info_full);

        log::info!("Config::update() = {:#?}", self);

        fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
            value.pointer(pointer).and_then(|it| T::deserialize(it).ok())
        }

        fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) {
            if let Some(new_value) = get(value, pointer) {
                *slot = new_value
            }
        }
    }

    pub fn update_caps(&mut self, caps: &TextDocumentClientCapabilities) {
        if let Some(value) = caps.definition.as_ref().and_then(|it| it.link_support) {
            self.client_caps.location_link = value;
        }
        if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
            self.client_caps.line_folding_only = value
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// 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.

#[macro_use]
extern crate log;
extern crate protobuf;
extern crate time;
extern crate zmq;

mod message;

use message::event::EventEnvelope;
use protobuf::parse_from_bytes;
use std::collections::HashMap;
use std::collections::HashSet;
use zmq::{Context, PULL, XPUB};

/// Proxies messages coming into `frontend_port` out through
/// `backend_port`, caching recent messages for new subscribers.
///
/// Event publishers should connect a ZMQ `PUSH` socket to
/// `frontend_port` and send `EventEnvelope` protobuf messages to
/// it. Publishers can connect to multiple such proxy processes;
/// messages will be fairly-dealt to all connected proxies.
///
/// Event subscribers should connect a ZMQ `SUB` socket to
/// `backend_port` to receive `EventEnvelope` protobuf
/// messages. Subscribers should connect to all available proxy
/// processes to ensure they receive all event messages.
///
/// Upon connection, subscribers will receive, from each connected
/// proxy, the most recent messages from each service and from each
/// ring member. Subscribers are responsible for sorting the messages
/// received by timestamp.
///
/// # Panics
///
/// If either `frontend_port` or `backend_port` cannot be bound to
/// sockets (e.g., they're already in use), the thread will panic.
pub fn proxy(frontend_port: i32, backend_port: i32) {
    let ctx = Context::new();

    let pull_sock = ctx.socket(PULL).unwrap();
    let pull_bind = format!("tcp://*:{}", frontend_port);
    match pull_sock.bind(&pull_bind) {
        Ok(()) => {},
        Err(e) => panic!(format!("Could not bind socket to port {}: {:?}", frontend_port, e))
    }

    let xpub_sock = ctx.socket(XPUB).unwrap();
    let xpub_bind = format!("tcp://*:{}", backend_port);
    match xpub_sock.bind(&xpub_bind) {
        Ok(()) => {},
        Err(e) => panic!(format!("Could not bind socket to port {}: {:?}", backend_port, e))
    }

    // We'll cache the most recent messages from each service and each
    // ring member. When new subscribers connect, we can send them
    // this "snapshot" of current activity.
    let mut service_cache = HashMap::new();
    let mut member_cache = HashMap::new();

    let mut poll_items = [pull_sock.as_poll_item(zmq::POLLIN),
                          xpub_sock.as_poll_item(zmq::POLLIN)];

    loop {
        // A timeout of -1 says to wait indefinitely until a message comes
        if zmq::poll(&mut poll_items, -1).is_err() {
            break; // This will stop the event service
        }

        if poll_items[0].is_readable() {
            // An event was published!

            let bytes = pull_sock.recv_bytes(0).unwrap();
            let event = parse_from_bytes::<EventEnvelope>(&bytes).unwrap();
            let member_id = event.get_member_id();
            let service = event.get_service().to_string();
            if service.is_empty() {
                warn!("missing service: {:?}", event);
                continue;
            }

            // Store the bytes of the message in the cache. For the
            // service cache, we also record the member ID, and vice
            // versa for the member cache; these data will be used for
            // deduplication of messages being sent to new subscribers.
            service_cache.insert(service.clone(), (member_id, bytes.clone()));
            member_cache.insert(member_id, (service, bytes.clone()));

            xpub_sock.send(&bytes, 0).unwrap();
        }

        if poll_items[1].is_readable() {
            // A subscriber connected; let's ensure they've got a
            // snapshot of what's currently happening.

            // Event is one byte 0=unsub or 1=sub, followed by topic
            let event = xpub_sock.recv_bytes(0).unwrap();
            if event[0] == 1 {
                // The subscriber has subscribed. Send all unique
                // cached messages to it.
                //
                // First we'll send all the latest messages from the
                // services, keeping track of which ring members those
                // were from. Then, we'll send the latest messages
                // from the members, but only if we didn't just send a
                // service message from them. This prevents us from
                // sending the same message twice.

                let mut members_encountered = HashSet::new();

                for (_, &(member_id, ref message)) in &service_cache {
                    members_encountered.insert(member_id);
                    xpub_sock.send(&message, 0).unwrap();
                }
                for (member_id, &(_, ref message)) in &member_cache {
                    if !(members_encountered.contains(member_id)) {
                        xpub_sock.send(&message, 0).unwrap();
                    }
                }
            }
        }
    }
}
<commit_msg>Cleanup panics<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// 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.

#[macro_use]
extern crate log;
extern crate protobuf;
extern crate time;
extern crate zmq;

mod message;

use message::event::EventEnvelope;
use protobuf::parse_from_bytes;
use std::collections::HashMap;
use std::collections::HashSet;
use zmq::{Context, PULL, XPUB};

/// Proxies messages coming into `frontend_port` out through
/// `backend_port`, caching recent messages for new subscribers.
///
/// Event publishers should connect a ZMQ `PUSH` socket to
/// `frontend_port` and send `EventEnvelope` protobuf messages to
/// it. Publishers can connect to multiple such proxy processes;
/// messages will be fairly-dealt to all connected proxies.
///
/// Event subscribers should connect a ZMQ `SUB` socket to
/// `backend_port` to receive `EventEnvelope` protobuf
/// messages. Subscribers should connect to all available proxy
/// processes to ensure they receive all event messages.
///
/// Upon connection, subscribers will receive, from each connected
/// proxy, the most recent messages from each service and from each
/// ring member. Subscribers are responsible for sorting the messages
/// received by timestamp.
///
/// # Panics
///
/// If either `frontend_port` or `backend_port` cannot be bound to
/// sockets (e.g., they're already in use), the thread will panic.
pub fn proxy(frontend_port: i32, backend_port: i32) {
    let ctx = Context::new();

    let pull_sock = ctx.socket(PULL).unwrap();
    let pull_bind = format!("tcp://*:{}", frontend_port);
    if let Err(e) = pull_sock.bind(&pull_bind) {
        panic!("Could not bind socket to port {}: {:?}", frontend_port, e);
    }

    let xpub_sock = ctx.socket(XPUB).unwrap();
    let xpub_bind = format!("tcp://*:{}", backend_port);
    if let Err(e) = xpub_sock.bind(&xpub_bind) {
        panic!("Could not bind socket to port {}: {:?}", backend_port, e);
    }

    // We'll cache the most recent messages from each service and each
    // ring member. When new subscribers connect, we can send them
    // this "snapshot" of current activity.
    let mut service_cache = HashMap::new();
    let mut member_cache = HashMap::new();

    let mut poll_items = [pull_sock.as_poll_item(zmq::POLLIN),
                          xpub_sock.as_poll_item(zmq::POLLIN)];

    loop {
        // A timeout of -1 says to wait indefinitely until a message comes
        if let Err(e) = zmq::poll(&mut poll_items, -1) {
            panic!("Error!: {}", e)
        }

        if poll_items[0].is_readable() {
            // An event was published!

            let bytes = pull_sock.recv_bytes(0).unwrap();
            let event = parse_from_bytes::<EventEnvelope>(&bytes).unwrap();
            let member_id = event.get_member_id();
            let service = event.get_service().to_string();
            if service.is_empty() {
                warn!("missing service: {:?}", event);
                continue;
            }

            // Store the bytes of the message in the cache. For the
            // service cache, we also record the member ID, and vice
            // versa for the member cache; these data will be used for
            // deduplication of messages being sent to new subscribers.
            service_cache.insert(service.clone(), (member_id, bytes.clone()));
            member_cache.insert(member_id, (service, bytes.clone()));

            xpub_sock.send(&bytes, 0).unwrap();
        }

        if poll_items[1].is_readable() {
            // A subscriber connected; let's ensure they've got a
            // snapshot of what's currently happening.

            // Event is one byte 0=unsub or 1=sub, followed by topic
            let event = xpub_sock.recv_bytes(0).unwrap();
            if event[0] == 1 {
                // The subscriber has subscribed. Send all unique
                // cached messages to it.
                //
                // First we'll send all the latest messages from the
                // services, keeping track of which ring members those
                // were from. Then, we'll send the latest messages
                // from the members, but only if we didn't just send a
                // service message from them. This prevents us from
                // sending the same message twice.

                let mut members_encountered = HashSet::new();

                for (_, &(member_id, ref message)) in &service_cache {
                    members_encountered.insert(member_id);
                    xpub_sock.send(&message, 0).unwrap();
                }
                for (member_id, &(_, ref message)) in &member_cache {
                    if !(members_encountered.contains(member_id)) {
                        xpub_sock.send(&message, 0).unwrap();
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! An algorithm to find a path to refer to a certain item.

use crate::{
    db::DefDatabase,
    item_scope::ItemInNs,
    path::{ModPath, PathKind},
    visibility::Visibility,
    CrateId, ModuleDefId, ModuleId,
};
use hir_expand::name::Name;

pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
    // Base cases:

    // - if the item is already in scope, return the name under which it is
    let def_map = db.crate_def_map(from.krate);
    let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
    if let Some((name, _)) = from_scope.reverse_get(item) {
        return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
    }

    // - if the item is the crate root, return `crate`
    if item
        == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
            krate: from.krate,
            local_id: def_map.root,
        }))
    {
        return Some(ModPath::from_simple_segments(PathKind::Crate, Vec::new()));
    }

    // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
    if let Some(parent_id) = def_map.modules[from.local_id].parent {
        if item
            == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
                krate: from.krate,
                local_id: parent_id,
            }))
        {
            return Some(ModPath::from_simple_segments(PathKind::Super(1), Vec::new()));
        }
    }

    // - if the item is the crate root of a dependency crate, return the name from the extern prelude
    for (name, def_id) in &def_map.extern_prelude {
        if item == ItemInNs::Types(*def_id) {
            return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
        }
    }

    // - if the item is in the prelude, return the name from there
    if let Some(prelude_module) = def_map.prelude {
        let prelude_def_map = db.crate_def_map(prelude_module.krate);
        let prelude_scope: &crate::item_scope::ItemScope =
            &prelude_def_map.modules[prelude_module.local_id].scope;
        if let Some((name, vis)) = prelude_scope.reverse_get(item) {
            if vis.is_visible_from(db, from) {
                return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
            }
        }
    }

    // Recursive case:
    // - if the item is an enum variant, refer to it via the enum
    if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
        if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) {
            let data = db.enum_data(variant.parent);
            path.segments.push(data.variants[variant.local_id].name.clone());
            return Some(path);
        }
        // If this doesn't work, it seems we have no way of referring to the
        // enum; that's very weird, but there might still be a reexport of the
        // variant somewhere
    }

    // - otherwise, look for modules containing (reexporting) it and import it from one of those
    let importable_locations = find_importable_locations(db, item, from);
    let mut candidate_paths = Vec::new();
    for (module_id, name) in importable_locations {
        // TODO prevent infinite loops
        let mut path = match find_path(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from)
        {
            None => continue,
            Some(path) => path,
        };
        path.segments.push(name);
        candidate_paths.push(path);
    }
    candidate_paths.into_iter().min_by_key(|path| path.segments.len())
}

fn find_importable_locations(
    db: &impl DefDatabase,
    item: ItemInNs,
    from: ModuleId,
) -> Vec<(ModuleId, Name)> {
    let crate_graph = db.crate_graph();
    let mut result = Vec::new();
    for krate in Some(from.krate)
        .into_iter()
        .chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id))
    {
        result.extend(
            db.importable_locations_in_crate(item, krate)
                .iter()
                .filter(|(_, _, vis)| vis.is_visible_from(db, from))
                .map(|(m, n, _)| (*m, n.clone())),
        );
    }
    result
}

pub(crate) fn importable_locations_in_crate_query(
    db: &impl DefDatabase,
    item: ItemInNs,
    krate: CrateId,
) -> std::sync::Arc<[(ModuleId, Name, Visibility)]> {
    let def_map = db.crate_def_map(krate);
    let mut result = Vec::new();
    for (local_id, data) in def_map.modules.iter() {
        if let Some((name, vis)) = data.scope.reverse_get(item) {
            let is_private = if let Visibility::Module(private_to) = vis {
                private_to.local_id == local_id
            } else {
                false
            };
            let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
                data.scope.declarations().any(|it| it == module_def_id)
            } else {
                false
            };
            if is_private && !is_original_def {
                // Ignore private imports. these could be used if we are
                // in a submodule of this module, but that's usually not
                // what the user wants; and if this module can import
                // the item and we're a submodule of it, so can we.
                // Also this keeps the cached data smaller.
                continue;
            }
            result.push((ModuleId { krate, local_id }, name.clone(), vis));
        }
    }
    result.into()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_db::TestDB;
    use hir_expand::hygiene::Hygiene;
    use ra_db::fixture::WithFixture;
    use ra_syntax::ast::AstNode;

    /// `code` needs to contain a cursor marker; checks that `find_path` for the
    /// item the `path` refers to returns that same path when called from the
    /// module the cursor is in.
    fn check_found_path(code: &str, path: &str) {
        let (db, pos) = TestDB::with_position(code);
        let module = db.module_for_file(pos.file_id);
        let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
        let ast_path = parsed_path_file
            .syntax_node()
            .descendants()
            .find_map(ra_syntax::ast::Path::cast)
            .unwrap();
        let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();

        let crate_def_map = db.crate_def_map(module.krate);
        let resolved = crate_def_map
            .resolve_path(
                &db,
                module.local_id,
                &mod_path,
                crate::item_scope::BuiltinShadowMode::Module,
            )
            .0
            .take_types()
            .unwrap();

        let found_path = find_path(&db, ItemInNs::Types(resolved), module);

        assert_eq!(found_path, Some(mod_path));
    }

    #[test]
    fn same_module() {
        let code = r#"
            //- /main.rs
            struct S;
            <|>
        "#;
        check_found_path(code, "S");
    }

    #[test]
    fn enum_variant() {
        let code = r#"
            //- /main.rs
            enum E { A }
            <|>
        "#;
        check_found_path(code, "E::A");
    }

    #[test]
    fn sub_module() {
        let code = r#"
            //- /main.rs
            mod foo {
                pub struct S;
            }
            <|>
        "#;
        check_found_path(code, "foo::S");
    }

    #[test]
    fn super_module() {
        let code = r#"
            //- /main.rs
            mod foo;
            //- /foo.rs
            mod bar;
            struct S;
            //- /foo/bar.rs
            <|>
        "#;
        check_found_path(code, "super::S");
    }

    #[test]
    fn crate_root() {
        let code = r#"
            //- /main.rs
            mod foo;
            //- /foo.rs
            <|>
        "#;
        check_found_path(code, "crate");
    }

    #[test]
    fn same_crate() {
        let code = r#"
            //- /main.rs
            mod foo;
            struct S;
            //- /foo.rs
            <|>
        "#;
        check_found_path(code, "crate::S");
    }

    #[test]
    fn different_crate() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub struct S;
        "#;
        check_found_path(code, "std::S");
    }

    #[test]
    fn different_crate_renamed() {
        let code = r#"
            //- /main.rs crate:main deps:std
            extern crate std as std_renamed;
            <|>
            //- /std.rs crate:std
            pub struct S;
        "#;
        check_found_path(code, "std_renamed::S");
    }

    #[test]
    fn same_crate_reexport() {
        let code = r#"
            //- /main.rs
            mod bar {
                mod foo { pub(super) struct S; }
                pub(crate) use foo::*;
            }
            <|>
        "#;
        check_found_path(code, "bar::S");
    }

    #[test]
    fn same_crate_reexport_rename() {
        let code = r#"
            //- /main.rs
            mod bar {
                mod foo { pub(super) struct S; }
                pub(crate) use foo::S as U;
            }
            <|>
        "#;
        check_found_path(code, "bar::U");
    }

    #[test]
    fn different_crate_reexport() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std deps:core
            pub use core::S;
            //- /core.rs crate:core
            pub struct S;
        "#;
        check_found_path(code, "std::S");
    }

    #[test]
    fn prelude() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub mod prelude { pub struct S; }
            #[prelude_import]
            pub use prelude::*;
        "#;
        check_found_path(code, "S");
    }

    #[test]
    fn enum_variant_from_prelude() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub mod prelude {
                pub enum Option<T> { Some(T), None }
                pub use Option::*;
            }
            #[prelude_import]
            pub use prelude::*;
        "#;
        check_found_path(code, "None");
        check_found_path(code, "Some");
    }

    #[test]
    fn shortest_path() {
        let code = r#"
            //- /main.rs
            pub mod foo;
            pub mod baz;
            struct S;
            <|>
            //- /foo.rs
            pub mod bar { pub struct S; }
            //- /baz.rs
            pub use crate::foo::bar::S;
        "#;
        check_found_path(code, "baz::S");
    }

    #[test]
    fn discount_private_imports() {
        let code = r#"
            //- /main.rs
            mod foo;
            pub mod bar { pub struct S; }
            use bar::S;
            //- /foo.rs
            <|>
        "#;
        // crate::S would be shorter, but using private imports seems wrong
        check_found_path(code, "crate::bar::S");
    }
}
<commit_msg>Handle cycles<commit_after>//! An algorithm to find a path to refer to a certain item.

use crate::{
    db::DefDatabase,
    item_scope::ItemInNs,
    path::{ModPath, PathKind},
    visibility::Visibility,
    CrateId, ModuleDefId, ModuleId,
};
use hir_expand::name::Name;

pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
    find_path_inner(db, item, from, 15)
}

fn find_path_inner(db: &impl DefDatabase, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath> {
    // Base cases:

    if max_len == 0 {
        return None;
    }

    // - if the item is already in scope, return the name under which it is
    let def_map = db.crate_def_map(from.krate);
    let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
    if let Some((name, _)) = from_scope.reverse_get(item) {
        return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
    }

    // - if the item is the crate root, return `crate`
    if item
        == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
            krate: from.krate,
            local_id: def_map.root,
        }))
    {
        return Some(ModPath::from_simple_segments(PathKind::Crate, Vec::new()));
    }

    // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
    if let Some(parent_id) = def_map.modules[from.local_id].parent {
        if item
            == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
                krate: from.krate,
                local_id: parent_id,
            }))
        {
            return Some(ModPath::from_simple_segments(PathKind::Super(1), Vec::new()));
        }
    }

    // - if the item is the crate root of a dependency crate, return the name from the extern prelude
    for (name, def_id) in &def_map.extern_prelude {
        if item == ItemInNs::Types(*def_id) {
            return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
        }
    }

    // - if the item is in the prelude, return the name from there
    if let Some(prelude_module) = def_map.prelude {
        let prelude_def_map = db.crate_def_map(prelude_module.krate);
        let prelude_scope: &crate::item_scope::ItemScope =
            &prelude_def_map.modules[prelude_module.local_id].scope;
        if let Some((name, vis)) = prelude_scope.reverse_get(item) {
            if vis.is_visible_from(db, from) {
                return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
            }
        }
    }

    // Recursive case:
    // - if the item is an enum variant, refer to it via the enum
    if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
        if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) {
            let data = db.enum_data(variant.parent);
            path.segments.push(data.variants[variant.local_id].name.clone());
            return Some(path);
        }
        // If this doesn't work, it seems we have no way of referring to the
        // enum; that's very weird, but there might still be a reexport of the
        // variant somewhere
    }

    // - otherwise, look for modules containing (reexporting) it and import it from one of those
    let importable_locations = find_importable_locations(db, item, from);
    let mut best_path = None;
    let mut best_path_len = max_len;
    for (module_id, name) in importable_locations {
        let mut path = match find_path_inner(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from, best_path_len - 1)
        {
            None => continue,
            Some(path) => path,
        };
        path.segments.push(name);
        if path_len(&path) < best_path_len {
            best_path_len = path_len(&path);
            best_path = Some(path);
        }
    }
    best_path
}

fn path_len(path: &ModPath) -> usize {
    path.segments.len() + match path.kind {
        PathKind::Plain => 0,
        PathKind::Super(i) => i as usize,
        PathKind::Crate => 1,
        PathKind::Abs => 0,
        PathKind::DollarCrate(_) => 1,
    }
}

fn find_importable_locations(
    db: &impl DefDatabase,
    item: ItemInNs,
    from: ModuleId,
) -> Vec<(ModuleId, Name)> {
    let crate_graph = db.crate_graph();
    let mut result = Vec::new();
    // We only look in the crate from which we are importing, and the direct
    // dependencies. We cannot refer to names from transitive dependencies
    // directly (only through reexports in direct dependencies).
    for krate in Some(from.krate)
        .into_iter()
        .chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id))
    {
        result.extend(
            db.importable_locations_in_crate(item, krate)
                .iter()
                .filter(|(_, _, vis)| vis.is_visible_from(db, from))
                .map(|(m, n, _)| (*m, n.clone())),
        );
    }
    result
}

/// Collects all locations from which we might import the item in a particular
/// crate. These include the original definition of the item, and any
/// non-private `use`s.
///
/// Note that the crate doesn't need to be the one in which the item is defined;
/// it might be re-exported in other crates. We cache this as a query since we
/// need to walk the whole def map for it.
pub(crate) fn importable_locations_in_crate_query(
    db: &impl DefDatabase,
    item: ItemInNs,
    krate: CrateId,
) -> std::sync::Arc<[(ModuleId, Name, Visibility)]> {
    let def_map = db.crate_def_map(krate);
    let mut result = Vec::new();
    for (local_id, data) in def_map.modules.iter() {
        if let Some((name, vis)) = data.scope.reverse_get(item) {
            let is_private = if let Visibility::Module(private_to) = vis {
                private_to.local_id == local_id
            } else {
                false
            };
            let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
                data.scope.declarations().any(|it| it == module_def_id)
            } else {
                false
            };
            if is_private && !is_original_def {
                // Ignore private imports. these could be used if we are
                // in a submodule of this module, but that's usually not
                // what the user wants; and if this module can import
                // the item and we're a submodule of it, so can we.
                // Also this keeps the cached data smaller.
                continue;
            }
            result.push((ModuleId { krate, local_id }, name.clone(), vis));
        }
    }
    result.into()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_db::TestDB;
    use hir_expand::hygiene::Hygiene;
    use ra_db::fixture::WithFixture;
    use ra_syntax::ast::AstNode;

    /// `code` needs to contain a cursor marker; checks that `find_path` for the
    /// item the `path` refers to returns that same path when called from the
    /// module the cursor is in.
    fn check_found_path(code: &str, path: &str) {
        let (db, pos) = TestDB::with_position(code);
        let module = db.module_for_file(pos.file_id);
        let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
        let ast_path = parsed_path_file
            .syntax_node()
            .descendants()
            .find_map(ra_syntax::ast::Path::cast)
            .unwrap();
        let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();

        let crate_def_map = db.crate_def_map(module.krate);
        let resolved = crate_def_map
            .resolve_path(
                &db,
                module.local_id,
                &mod_path,
                crate::item_scope::BuiltinShadowMode::Module,
            )
            .0
            .take_types()
            .unwrap();

        let found_path = find_path(&db, ItemInNs::Types(resolved), module);

        assert_eq!(found_path, Some(mod_path));
    }

    #[test]
    fn same_module() {
        let code = r#"
            //- /main.rs
            struct S;
            <|>
        "#;
        check_found_path(code, "S");
    }

    #[test]
    fn enum_variant() {
        let code = r#"
            //- /main.rs
            enum E { A }
            <|>
        "#;
        check_found_path(code, "E::A");
    }

    #[test]
    fn sub_module() {
        let code = r#"
            //- /main.rs
            mod foo {
                pub struct S;
            }
            <|>
        "#;
        check_found_path(code, "foo::S");
    }

    #[test]
    fn super_module() {
        let code = r#"
            //- /main.rs
            mod foo;
            //- /foo.rs
            mod bar;
            struct S;
            //- /foo/bar.rs
            <|>
        "#;
        check_found_path(code, "super::S");
    }

    #[test]
    fn crate_root() {
        let code = r#"
            //- /main.rs
            mod foo;
            //- /foo.rs
            <|>
        "#;
        check_found_path(code, "crate");
    }

    #[test]
    fn same_crate() {
        let code = r#"
            //- /main.rs
            mod foo;
            struct S;
            //- /foo.rs
            <|>
        "#;
        check_found_path(code, "crate::S");
    }

    #[test]
    fn different_crate() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub struct S;
        "#;
        check_found_path(code, "std::S");
    }

    #[test]
    fn different_crate_renamed() {
        let code = r#"
            //- /main.rs crate:main deps:std
            extern crate std as std_renamed;
            <|>
            //- /std.rs crate:std
            pub struct S;
        "#;
        check_found_path(code, "std_renamed::S");
    }

    #[test]
    fn same_crate_reexport() {
        let code = r#"
            //- /main.rs
            mod bar {
                mod foo { pub(super) struct S; }
                pub(crate) use foo::*;
            }
            <|>
        "#;
        check_found_path(code, "bar::S");
    }

    #[test]
    fn same_crate_reexport_rename() {
        let code = r#"
            //- /main.rs
            mod bar {
                mod foo { pub(super) struct S; }
                pub(crate) use foo::S as U;
            }
            <|>
        "#;
        check_found_path(code, "bar::U");
    }

    #[test]
    fn different_crate_reexport() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std deps:core
            pub use core::S;
            //- /core.rs crate:core
            pub struct S;
        "#;
        check_found_path(code, "std::S");
    }

    #[test]
    fn prelude() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub mod prelude { pub struct S; }
            #[prelude_import]
            pub use prelude::*;
        "#;
        check_found_path(code, "S");
    }

    #[test]
    fn enum_variant_from_prelude() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub mod prelude {
                pub enum Option<T> { Some(T), None }
                pub use Option::*;
            }
            #[prelude_import]
            pub use prelude::*;
        "#;
        check_found_path(code, "None");
        check_found_path(code, "Some");
    }

    #[test]
    fn shortest_path() {
        let code = r#"
            //- /main.rs
            pub mod foo;
            pub mod baz;
            struct S;
            <|>
            //- /foo.rs
            pub mod bar { pub struct S; }
            //- /baz.rs
            pub use crate::foo::bar::S;
        "#;
        check_found_path(code, "baz::S");
    }

    #[test]
    fn discount_private_imports() {
        let code = r#"
            //- /main.rs
            mod foo;
            pub mod bar { pub struct S; }
            use bar::S;
            //- /foo.rs
            <|>
        "#;
        // crate::S would be shorter, but using private imports seems wrong
        check_found_path(code, "crate::bar::S");
    }

    #[test]
    fn import_cycle() {
        let code = r#"
            //- /main.rs
            pub mod foo;
            pub mod bar;
            pub mod baz;
            //- /bar.rs
            <|>
            //- /foo.rs
            pub use super::baz;
            pub struct S;
            //- /baz.rs
            pub use super::foo;
        "#;
        check_found_path(code, "crate::foo::S");
    }
}
<|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/. */

//! Common handling of keyboard input and state management for text input controls

use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
use dom::bindings::js::JSRef;
use dom::keyboardevent::KeyboardEvent;
use servo_util::str::DOMString;

use std::cmp::{min, max};
use std::default::Default;

#[jstraceable]
struct TextPoint {
    /// 0-based line number
    line: uint,
    /// 0-based column number
    index: uint,
}

/// Encapsulated state for handling keyboard input in a single or multiline text input control.
#[jstraceable]
pub struct TextInput {
    /// Current text input content, split across lines without trailing '\n'
    lines: Vec<DOMString>,
    /// Current cursor input point
    edit_point: TextPoint,
    /// Beginning of selection range with edit_point as end that can span multiple lines.
    selection_begin: Option<TextPoint>,
    /// Is this a multiline input?
    multiline: bool,
}

/// Resulting action to be taken by the owner of a text input that is handling an event.
pub enum KeyReaction {
    TriggerDefaultAction,
    DispatchInput,
    Nothing,
}

impl Default for TextPoint {
    fn default() -> TextPoint {
        TextPoint {
            line: 0,
            index: 0,
        }
    }
}

/// Control whether this control should allow multiple lines.
#[deriving(PartialEq)]
pub enum Lines {
    Single,
    Multiple,
}

/// The direction in which to delete a character.
#[deriving(PartialEq)]
enum DeleteDir {
    Forward,
    Backward
}

impl TextInput {
    /// Instantiate a new text input control
    pub fn new(lines: Lines, initial: DOMString) -> TextInput {
        let mut i = TextInput {
            lines: vec!(),
            edit_point: Default::default(),
            selection_begin: None,
            multiline: lines == Multiple,
        };
        i.set_content(initial);
        i
    }

    /// Remove a character at the current editing point
    fn delete_char(&mut self, dir: DeleteDir) {
        if self.selection_begin.is_none() {
            self.adjust_horizontal(if dir == Forward {1} else {-1}, true);
        }
        self.replace_selection("".to_string());
    }

    /// Insert a character at the current editing point
    fn insert_char(&mut self, ch: char) {
        if self.selection_begin.is_none() {
            self.selection_begin = Some(self.edit_point);
        }
        self.replace_selection(ch.to_string());
    }

    fn replace_selection(&mut self, insert: String) {
        let begin = self.selection_begin.take().unwrap();
        let end = self.edit_point;

        let (begin, end) = if begin.line < end.line || (begin.line == end.line && begin.index < end.index) {
            (begin, end)
        } else {
            (end, begin)
        };

        let new_lines = {
            let prefix = self.lines[begin.line].slice_chars(0, begin.index);
            let suffix = self.lines[end.line].slice_chars(end.index, self.lines[end.line].char_len());
            let lines_prefix = self.lines.slice(0, begin.line);
            let lines_suffix = self.lines.slice(end.line + 1, self.lines.len());

            let mut insert_lines = if self.multiline {
                insert.as_slice().split('\n').map(|s| s.to_string()).collect()
            } else {
                vec!(insert)
            };

            let mut new_line = prefix.to_string();
            new_line.push_str(insert_lines[0].as_slice());
            insert_lines[0] = new_line;

            let last_insert_lines_index = insert_lines.len() - 1;
            self.edit_point.index = insert_lines[last_insert_lines_index].char_len();
            self.edit_point.line = begin.line + last_insert_lines_index;

            insert_lines[last_insert_lines_index].push_str(suffix);

            let mut new_lines = vec!();
            new_lines.push_all(lines_prefix);
            new_lines.push_all(insert_lines.as_slice());
            new_lines.push_all(lines_suffix);
            new_lines
        };

        self.lines = new_lines;
    }

    /// Return the length of the current line under the editing point.
    fn current_line_length(&self) -> uint {
        self.lines[self.edit_point.line].char_len()
    }

    /// Adjust the editing point position by a given of lines. The resulting column is
    /// as close to the original column position as possible.
    fn adjust_vertical(&mut self, adjust: int, select: bool) {
        if !self.multiline {
            return;
        }

        if select {
            if self.selection_begin.is_none() {
                self.selection_begin = Some(self.edit_point);
            }
        } else {
            self.selection_begin = None;
        }

        assert!(self.edit_point.line < self.lines.len());

        let target_line: int = self.edit_point.line as int + adjust;

        if target_line < 0 {
            self.edit_point.index = 0;
            self.edit_point.line = 0;
            return;
        } else if target_line as uint >= self.lines.len() {
            self.edit_point.line = self.lines.len() - 1;
            self.edit_point.index = self.current_line_length();
            return;
        }

        self.edit_point.line = target_line as uint;
        self.edit_point.index = min(self.current_line_length(), self.edit_point.index);
    }

    /// Adjust the editing point position by a given number of columns. If the adjustment
    /// requested is larger than is available in the current line, the editing point is
    /// adjusted vertically and the process repeats with the remaining adjustment requested.
    fn adjust_horizontal(&mut self, adjust: int, select: bool) {
        if select {
            if self.selection_begin.is_none() {
                self.selection_begin = Some(self.edit_point);
            }
        } else {
            self.selection_begin = None;
        }

        if adjust < 0 {
            let remaining = self.edit_point.index;
            if adjust.abs() as uint > remaining && self.edit_point.line > 0 {
                self.adjust_vertical(-1, select);
                self.edit_point.index = self.current_line_length();
                self.adjust_horizontal(adjust + remaining as int + 1, select);
            } else {
                self.edit_point.index = max(0, self.edit_point.index as int + adjust) as uint;
            }
        } else {
            let remaining = self.current_line_length() - self.edit_point.index;
            if adjust as uint > remaining && self.lines.len() > self.edit_point.line + 1 {
                self.adjust_vertical(1, select);
                self.edit_point.index = 0;
                // one shift is consumed by the change of line, hence the -1
                self.adjust_horizontal(adjust - remaining as int - 1, select);
            } else {
                self.edit_point.index = min(self.current_line_length(),
                                            self.edit_point.index + adjust as uint);
            }
        }
    }

    /// Deal with a newline input.
    fn handle_return(&mut self) -> KeyReaction {
        if !self.multiline {
            return TriggerDefaultAction;
        }
        self.insert_char('\n');
        return DispatchInput;
    }

    /// Process a given `KeyboardEvent` and return an action for the caller to execute.
    pub fn handle_keydown(&mut self, event: JSRef<KeyboardEvent>) -> KeyReaction {
        match event.Key().as_slice() {
            // printable characters have single-character key values
            c if c.len() == 1 => {
                self.insert_char(c.char_at(0));
                return DispatchInput;
            }
            "Space" => {
                self.insert_char(' ');
                DispatchInput
            }
            "Delete" => {
                self.delete_char(Forward);
                DispatchInput
            }
            "Backspace" => {
                self.delete_char(Backward);
                DispatchInput
            }
            "ArrowLeft" => {
                self.adjust_horizontal(-1, event.ShiftKey());
                Nothing
            }
            "ArrowRight" => {
                self.adjust_horizontal(1, event.ShiftKey());
                Nothing
            }
            "ArrowUp" => {
                self.adjust_vertical(-1, event.ShiftKey());
                Nothing
            }
            "ArrowDown" => {
                self.adjust_vertical(1, event.ShiftKey());
                Nothing
            }
            "Enter" => self.handle_return(),
            "Home" => {
                self.edit_point.index = 0;
                Nothing
            }
            "End" => {
                self.edit_point.index = self.current_line_length();
                Nothing
            }
            "PageUp" => {
                self.adjust_vertical(-28, event.ShiftKey());
                Nothing
            }
            "PageDown" => {
                self.adjust_vertical(28, event.ShiftKey());
                Nothing
            }
            "Tab" => TriggerDefaultAction,
            _ => Nothing,
        }
    }

    /// Get the current contents of the text input. Multiple lines are joined by \n.
    pub fn get_content(&self) -> DOMString {
        let mut content = "".to_string();
        for (i, line) in self.lines.iter().enumerate() {
            content.push_str(line.as_slice());
            if i < self.lines.len() - 1 {
                content.push('\n');
            }
        }
        content
    }

    /// Set the current contents of the text input. If this is control supports multiple lines,
    /// any \n encountered will be stripped and force a new logical line.
    pub fn set_content(&mut self, content: DOMString) {
        self.lines = if self.multiline {
            content.as_slice().split('\n').map(|s| s.to_string()).collect()
        } else {
            vec!(content)
        };
        self.edit_point.line = min(self.edit_point.line, self.lines.len() - 1);
        self.edit_point.index = min(self.edit_point.index, self.current_line_length() - 1);
    }
}
<commit_msg>Fix crash in textinput<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/. */

//! Common handling of keyboard input and state management for text input controls

use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
use dom::bindings::js::JSRef;
use dom::keyboardevent::KeyboardEvent;
use servo_util::str::DOMString;

use std::cmp::{min, max};
use std::default::Default;

#[jstraceable]
struct TextPoint {
    /// 0-based line number
    line: uint,
    /// 0-based column number
    index: uint,
}

/// Encapsulated state for handling keyboard input in a single or multiline text input control.
#[jstraceable]
pub struct TextInput {
    /// Current text input content, split across lines without trailing '\n'
    lines: Vec<DOMString>,
    /// Current cursor input point
    edit_point: TextPoint,
    /// Beginning of selection range with edit_point as end that can span multiple lines.
    selection_begin: Option<TextPoint>,
    /// Is this a multiline input?
    multiline: bool,
}

/// Resulting action to be taken by the owner of a text input that is handling an event.
pub enum KeyReaction {
    TriggerDefaultAction,
    DispatchInput,
    Nothing,
}

impl Default for TextPoint {
    fn default() -> TextPoint {
        TextPoint {
            line: 0,
            index: 0,
        }
    }
}

/// Control whether this control should allow multiple lines.
#[deriving(PartialEq)]
pub enum Lines {
    Single,
    Multiple,
}

/// The direction in which to delete a character.
#[deriving(PartialEq)]
enum DeleteDir {
    Forward,
    Backward
}

impl TextInput {
    /// Instantiate a new text input control
    pub fn new(lines: Lines, initial: DOMString) -> TextInput {
        let mut i = TextInput {
            lines: vec!(),
            edit_point: Default::default(),
            selection_begin: None,
            multiline: lines == Multiple,
        };
        i.set_content(initial);
        i
    }

    /// Remove a character at the current editing point
    fn delete_char(&mut self, dir: DeleteDir) {
        if self.selection_begin.is_none() {
            self.adjust_horizontal(if dir == Forward {1} else {-1}, true);
        }
        self.replace_selection("".to_string());
    }

    /// Insert a character at the current editing point
    fn insert_char(&mut self, ch: char) {
        if self.selection_begin.is_none() {
            self.selection_begin = Some(self.edit_point);
        }
        self.replace_selection(ch.to_string());
    }

    fn replace_selection(&mut self, insert: String) {
        let begin = self.selection_begin.take().unwrap();
        let end = self.edit_point;

        let (begin, end) = if begin.line < end.line || (begin.line == end.line && begin.index < end.index) {
            (begin, end)
        } else {
            (end, begin)
        };

        let new_lines = {
            let prefix = self.lines[begin.line].slice_chars(0, begin.index);
            let suffix = self.lines[end.line].slice_chars(end.index, self.lines[end.line].char_len());
            let lines_prefix = self.lines.slice(0, begin.line);
            let lines_suffix = self.lines.slice(end.line + 1, self.lines.len());

            let mut insert_lines = if self.multiline {
                insert.as_slice().split('\n').map(|s| s.to_string()).collect()
            } else {
                vec!(insert)
            };

            let mut new_line = prefix.to_string();
            new_line.push_str(insert_lines[0].as_slice());
            insert_lines[0] = new_line;

            let last_insert_lines_index = insert_lines.len() - 1;
            self.edit_point.index = insert_lines[last_insert_lines_index].char_len();
            self.edit_point.line = begin.line + last_insert_lines_index;

            insert_lines[last_insert_lines_index].push_str(suffix);

            let mut new_lines = vec!();
            new_lines.push_all(lines_prefix);
            new_lines.push_all(insert_lines.as_slice());
            new_lines.push_all(lines_suffix);
            new_lines
        };

        self.lines = new_lines;
    }

    /// Return the length of the current line under the editing point.
    fn current_line_length(&self) -> uint {
        self.lines[self.edit_point.line].char_len()
    }

    /// Adjust the editing point position by a given of lines. The resulting column is
    /// as close to the original column position as possible.
    fn adjust_vertical(&mut self, adjust: int, select: bool) {
        if !self.multiline {
            return;
        }

        if select {
            if self.selection_begin.is_none() {
                self.selection_begin = Some(self.edit_point);
            }
        } else {
            self.selection_begin = None;
        }

        assert!(self.edit_point.line < self.lines.len());

        let target_line: int = self.edit_point.line as int + adjust;

        if target_line < 0 {
            self.edit_point.index = 0;
            self.edit_point.line = 0;
            return;
        } else if target_line as uint >= self.lines.len() {
            self.edit_point.line = self.lines.len() - 1;
            self.edit_point.index = self.current_line_length();
            return;
        }

        self.edit_point.line = target_line as uint;
        self.edit_point.index = min(self.current_line_length(), self.edit_point.index);
    }

    /// Adjust the editing point position by a given number of columns. If the adjustment
    /// requested is larger than is available in the current line, the editing point is
    /// adjusted vertically and the process repeats with the remaining adjustment requested.
    fn adjust_horizontal(&mut self, adjust: int, select: bool) {
        if select {
            if self.selection_begin.is_none() {
                self.selection_begin = Some(self.edit_point);
            }
        } else {
            self.selection_begin = None;
        }

        if adjust < 0 {
            let remaining = self.edit_point.index;
            if adjust.abs() as uint > remaining && self.edit_point.line > 0 {
                self.adjust_vertical(-1, select);
                self.edit_point.index = self.current_line_length();
                self.adjust_horizontal(adjust + remaining as int + 1, select);
            } else {
                self.edit_point.index = max(0, self.edit_point.index as int + adjust) as uint;
            }
        } else {
            let remaining = self.current_line_length() - self.edit_point.index;
            if adjust as uint > remaining && self.lines.len() > self.edit_point.line + 1 {
                self.adjust_vertical(1, select);
                self.edit_point.index = 0;
                // one shift is consumed by the change of line, hence the -1
                self.adjust_horizontal(adjust - remaining as int - 1, select);
            } else {
                self.edit_point.index = min(self.current_line_length(),
                                            self.edit_point.index + adjust as uint);
            }
        }
    }

    /// Deal with a newline input.
    fn handle_return(&mut self) -> KeyReaction {
        if !self.multiline {
            return TriggerDefaultAction;
        }
        self.insert_char('\n');
        return DispatchInput;
    }

    /// Process a given `KeyboardEvent` and return an action for the caller to execute.
    pub fn handle_keydown(&mut self, event: JSRef<KeyboardEvent>) -> KeyReaction {
        match event.Key().as_slice() {
            // printable characters have single-character key values
            c if c.len() == 1 => {
                self.insert_char(c.char_at(0));
                return DispatchInput;
            }
            "Space" => {
                self.insert_char(' ');
                DispatchInput
            }
            "Delete" => {
                self.delete_char(Forward);
                DispatchInput
            }
            "Backspace" => {
                self.delete_char(Backward);
                DispatchInput
            }
            "ArrowLeft" => {
                self.adjust_horizontal(-1, event.ShiftKey());
                Nothing
            }
            "ArrowRight" => {
                self.adjust_horizontal(1, event.ShiftKey());
                Nothing
            }
            "ArrowUp" => {
                self.adjust_vertical(-1, event.ShiftKey());
                Nothing
            }
            "ArrowDown" => {
                self.adjust_vertical(1, event.ShiftKey());
                Nothing
            }
            "Enter" => self.handle_return(),
            "Home" => {
                self.edit_point.index = 0;
                Nothing
            }
            "End" => {
                self.edit_point.index = self.current_line_length();
                Nothing
            }
            "PageUp" => {
                self.adjust_vertical(-28, event.ShiftKey());
                Nothing
            }
            "PageDown" => {
                self.adjust_vertical(28, event.ShiftKey());
                Nothing
            }
            "Tab" => TriggerDefaultAction,
            _ => Nothing,
        }
    }

    /// Get the current contents of the text input. Multiple lines are joined by \n.
    pub fn get_content(&self) -> DOMString {
        let mut content = "".to_string();
        for (i, line) in self.lines.iter().enumerate() {
            content.push_str(line.as_slice());
            if i < self.lines.len() - 1 {
                content.push('\n');
            }
        }
        content
    }

    /// Set the current contents of the text input. If this is control supports multiple lines,
    /// any \n encountered will be stripped and force a new logical line.
    pub fn set_content(&mut self, content: DOMString) {
        self.lines = if self.multiline {
            content.as_slice().split('\n').map(|s| s.to_string()).collect()
        } else {
            vec!(content)
        };
        self.edit_point.line = min(self.edit_point.line, self.lines.len() - 1);

        if self.current_line_length() == 0 {
            self.edit_point.index = 0;
        }
        else {
            self.edit_point.index = min(self.edit_point.index, self.current_line_length() - 1);
        }
    }
}
<|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 syntax::ast::MetaItem;
use syntax::codemap::Span;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ptr::P;

pub fn expand_dom_struct(cx: &mut ExtCtxt, sp: Span, _: &MetaItem, anno: Annotatable) -> Annotatable {
    if let Annotatable::Item(item) = anno {
        let mut item2 = (*item).clone();
        item2.attrs.push(quote_attr!(cx, #[must_root]));
        item2.attrs.push(quote_attr!(cx, #[privatize]));
        item2.attrs.push(quote_attr!(cx, #[derive(JSTraceable)]));
        item2.attrs.push(quote_attr!(cx, #[derive(HeapSizeOf)]));

        // The following attributes are only for internal usage
        item2.attrs.push(quote_attr!(cx, #[_generate_reflector]));
        // #[dom_struct] gets consumed, so this lets us keep around a residue
        // Do NOT register a modifier/decorator on this attribute
        item2.attrs.push(quote_attr!(cx, #[_dom_struct_marker]));
        Annotatable::Item(P(item2))
    } else {
        cx.span_err(sp, "#[dom_struct] applied to something other than a struct");
        anno
    }
}
<commit_msg>Auto merge of #14710 - nox:repr-c, r=KiChjang<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 syntax::ast::MetaItem;
use syntax::codemap::Span;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ptr::P;

pub fn expand_dom_struct(cx: &mut ExtCtxt, sp: Span, _: &MetaItem, anno: Annotatable) -> Annotatable {
    if let Annotatable::Item(item) = anno {
        let mut item2 = (*item).clone();
        item2.attrs.push(quote_attr!(cx, #[must_root]));
        item2.attrs.push(quote_attr!(cx, #[privatize]));
        item2.attrs.push(quote_attr!(cx, #[repr(C)]));
        item2.attrs.push(quote_attr!(cx, #[derive(JSTraceable)]));
        item2.attrs.push(quote_attr!(cx, #[derive(HeapSizeOf)]));

        // The following attributes are only for internal usage
        item2.attrs.push(quote_attr!(cx, #[_generate_reflector]));
        // #[dom_struct] gets consumed, so this lets us keep around a residue
        // Do NOT register a modifier/decorator on this attribute
        item2.attrs.push(quote_attr!(cx, #[_dom_struct_marker]));
        Annotatable::Item(P(item2))
    } else {
        cx.span_err(sp, "#[dom_struct] applied to something other than a struct");
        anno
    }
}
<|endoftext|>