<commit_before>use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, Filesystem};
use anyhow::Context as _;
use cargo_util::paths;
use lazycell::LazyCell;
use log::{debug, trace};
use std::cell::{Cell, Ref, RefCell};
use std::fs::File;
use std::mem;
use std::path::Path;
use std::str;
use std::task::Poll;

/// A remote registry is a registry that lives at a remote URL (such as
/// crates.io). The git index is cloned locally, and `.crate` files are
/// downloaded as needed and cached locally.
pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/path/$REG-HASH`).
    cache_path: Filesystem,
    source_id: SourceId,
    index_git_ref: GitReference,
    config: &'cfg Config,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
    current_sha: Cell<Option<InternedString>>,
    needs_update: bool, // Does this registry need to be updated?
    updated: bool,      // Has this registry been updated this session?
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id,
            config,
            // TODO: we should probably make this configurable
            index_git_ref: GitReference::DefaultBranch,
            tree: RefCell::new(None),
            repo: LazyCell::new(),
            head: Cell::new(None),
            current_sha: Cell::new(None),
            needs_update: false,
            updated: false,
        }
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.try_borrow_with(|| {
            let path = self.config.assert_package_cache_locked(&self.index_path);

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                trace!("opened a repo without a lock");
                return Ok(repo);
            }

            // Ok, now we need to lock and try the whole thing over again.
            trace!("acquiring registry index lock");
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    drop(paths::remove_dir_all(&path));
                    paths::create_dir_all(&path)?;

                    // Note that we'd actually prefer to use a bare repository
                    // here as we're not actually going to check anything out.
                    // All versions of Cargo, though, share the same CARGO_HOME,
                    // so for compatibility with older Cargo which *does* do
                    // checkouts we make sure to initialize a new full
                    // repository (not a bare one).
                    //
                    // We should change this to `init_bare` whenever we feel
                    // like enough time has passed or if we change the directory
                    // that the folder is located in, such as by changing the
                    // hash at the end of the directory.
                    //
                    // Note that in the meantime we also skip `init.templatedir`
                    // as it can be misconfigured sometimes or otherwise add
                    // things that we don't want.
                    let mut opts = git2::RepositoryInitOptions::new();
                    opts.external_template(false);
                    Ok(git2::Repository::init_opts(&path, &opts).with_context(|| {
                        format!("failed to initialize index git repository (in {:?})", path)
                    })?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let repo = self.repo()?;
            let oid = self.index_git_ref.resolve(repo)?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<'_, git2::Tree<'_>>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()));
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe { mem::transmute::<git2::Tree<'_>, git2::Tree<'static>>(tree) };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }

    fn current_version(&self) -> Option<InternedString> {
        if let Some(sha) = self.current_sha.get() {
            return Some(sha);
        }
        let sha = InternedString::new(&self.head().ok()?.to_string());
        self.current_sha.set(Some(sha));
        Some(sha)
    }
}

const LAST_UPDATED_FILE: &str = ".last-updated";

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn prepare(&self) -> CargoResult<()> {
        self.repo()?; // create intermediate dirs and initialize the repo
        Ok(())
    }

    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
        self.config.assert_package_cache_locked(path)
    }

    // `index_version` Is a string representing the version of the file used to construct the cached copy.
    // Older versions of Cargo used the single value of the hash of the HEAD commit as a `index_version`.
    // This is technically correct but a little too conservative. If a new commit is fetched all cached
    // files need to be regenerated even if a particular file was not changed.
    // Cargo now reads the `index_version` in two parts the cache file is considered valid if `index_version`
    // ends with the hash of the HEAD commit OR if it starts with the hash of the file's contents.
    // In the future cargo can write cached files with `index_version` = `git_file_hash + ":" + `git_commit_hash`,
    // but for now it still uses `git_commit_hash` to be compatible with older Cargoes.
    fn load(
        &mut self,
        _root: &Path,
        path: &Path,
        index_version: Option<&str>,
    ) -> Poll<CargoResult<LoadResponse>> {
        if self.needs_update {
            return Poll::Pending;
        }
        // Check if the cache is valid.
        let git_commit_hash = self.current_version();
        if let (Some(c), Some(i)) = (git_commit_hash, index_version) {
            if i.ends_with(c.as_str()) {
                return Poll::Ready(Ok(LoadResponse::CacheValid));
            }
        }
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        fn load_helper(
            registry: &RemoteRegistry<'_>,
            path: &Path,
            index_version: Option<&str>,
            git_commit_hash: Option<&str>,
        ) -> CargoResult<LoadResponse> {
            let repo = registry.repo()?;
            let tree = registry.tree()?;
            let entry = tree.get_path(path);
            let entry = entry?;
            let git_file_hash = entry.id().to_string();

            if let Some(i) = index_version {
                if i.starts_with(git_file_hash.as_str()) {
                    return Ok(LoadResponse::CacheValid);
                }
            }

            let object = entry.to_object(repo)?;
            let blob = match object.as_blob() {
                Some(blob) => blob,
                None => anyhow::bail!("path `{}` is not a blob in the git repo", path.display()),
            };

            Ok(LoadResponse::Data {
                raw_data: blob.content().to_vec(),
                index_version: git_commit_hash.map(String::from),
                // TODO: When the reading code has been stable for long enough (Say 8/2022)
                // change to `git_file_hash + ":" + git_commit_hash`
            })
        }

        match load_helper(&self, path, index_version, git_commit_hash.as_deref()) {
            Ok(result) => Poll::Ready(Ok(result)),
            Err(_) if !self.updated => {
                // If git returns an error and we haven't updated the repo, return
                // pending to allow an update to try again.
                self.needs_update = true;
                Poll::Pending
            }
            Err(e)
                if e.downcast_ref::<git2::Error>()
                    .map(|e| e.code() == git2::ErrorCode::NotFound)
                    .unwrap_or_default() =>
            {
                // The repo has been updated and the file does not exist.
                Poll::Ready(Ok(LoadResponse::NotFound))
            }
            Err(e) => Poll::Ready(Err(e)),
        }
    }

    fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
        debug!("loading config");
        self.prepare()?;
        self.config.assert_package_cache_locked(&self.index_path);
        match self.load(Path::new(""), Path::new("config.json"), None)? {
            Poll::Ready(LoadResponse::Data { raw_data, .. }) => {
                trace!("config loaded");
                Poll::Ready(Ok(Some(serde_json::from_slice(&raw_data)?)))
            }
            Poll::Ready(_) => Poll::Ready(Ok(None)),
            Poll::Pending => Poll::Pending,
        }
    }

    fn block_until_ready(&mut self) -> CargoResult<()> {
        if !self.needs_update {
            return Ok(());
        }

        self.updated = true;
        self.needs_update = false;

        if self.config.offline() {
            return Ok(());
        }
        if self.config.cli_unstable().no_index_update {
            return Ok(());
        }
        // Make sure the index is only updated once per session since it is an
        // expensive operation. This generally only happens when the resolver
        // is run multiple times, such as during `cargo publish`.
        if self.config.updated_sources().contains(&self.source_id) {
            return Ok(());
        }

        debug!("updating the index");

        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        self.config.http()?;

        self.prepare()?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        self.current_sha.set(None);
        let path = self.config.assert_package_cache_locked(&self.index_path);
        self.config
            .shell()
            .status("Updating", self.source_id.display_index())?;

        // Fetch the latest version of our `index_git_ref` into the index
        // checkout.
        let url = self.source_id.url();
        let repo = self.repo.borrow_mut().unwrap();
        git::fetch(repo, url.as_str(), &self.index_git_ref, self.config)
            .with_context(|| format!("failed to fetch `{}`", url))?;
        self.config.updated_sources().insert(self.source_id);

        // Create a dummy file to record the mtime for when we updated the
        // index.
        paths::create(&path.join(LAST_UPDATED_FILE))?;

        Ok(())
    }

    fn invalidate_cache(&mut self) {
        if !self.updated {
            self.needs_update = true;
        }
    }

    fn is_updated(&self) -> bool {
        self.updated
    }

    fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
        let registry_config = loop {
            match self.config()? {
                Poll::Pending => self.block_until_ready()?,
                Poll::Ready(cfg) => break cfg.unwrap(),
            }
        };

        download::download(
            &self.cache_path,
            &self.config,
            pkg,
            checksum,
            registry_config,
        )
    }

    fn finish_download(
        &mut self,
        pkg: PackageId,
        checksum: &str,
        data: &[u8],
    ) -> CargoResult<File> {
        download::finish_download(&self.cache_path, &self.config, pkg, checksum, data)
    }

    fn is_crate_downloaded(&self, pkg: PackageId) -> bool {
        download::is_crate_downloaded(&self.cache_path, &self.config, pkg)
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}
<commit_msg>Cache based on contents hash<commit_after>use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, Filesystem};
use anyhow::Context as _;
use cargo_util::paths;
use lazycell::LazyCell;
use log::{debug, trace};
use std::cell::{Cell, Ref, RefCell};
use std::fs::File;
use std::mem;
use std::path::Path;
use std::str;
use std::task::Poll;

/// A remote registry is a registry that lives at a remote URL (such as
/// crates.io). The git index is cloned locally, and `.crate` files are
/// downloaded as needed and cached locally.
pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/path/$REG-HASH`).
    cache_path: Filesystem,
    source_id: SourceId,
    index_git_ref: GitReference,
    config: &'cfg Config,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
    current_sha: Cell<Option<InternedString>>,
    needs_update: bool, // Does this registry need to be updated?
    updated: bool,      // Has this registry been updated this session?
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id,
            config,
            // TODO: we should probably make this configurable
            index_git_ref: GitReference::DefaultBranch,
            tree: RefCell::new(None),
            repo: LazyCell::new(),
            head: Cell::new(None),
            current_sha: Cell::new(None),
            needs_update: false,
            updated: false,
        }
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.try_borrow_with(|| {
            let path = self.config.assert_package_cache_locked(&self.index_path);

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                trace!("opened a repo without a lock");
                return Ok(repo);
            }

            // Ok, now we need to lock and try the whole thing over again.
            trace!("acquiring registry index lock");
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    drop(paths::remove_dir_all(&path));
                    paths::create_dir_all(&path)?;

                    // Note that we'd actually prefer to use a bare repository
                    // here as we're not actually going to check anything out.
                    // All versions of Cargo, though, share the same CARGO_HOME,
                    // so for compatibility with older Cargo which *does* do
                    // checkouts we make sure to initialize a new full
                    // repository (not a bare one).
                    //
                    // We should change this to `init_bare` whenever we feel
                    // like enough time has passed or if we change the directory
                    // that the folder is located in, such as by changing the
                    // hash at the end of the directory.
                    //
                    // Note that in the meantime we also skip `init.templatedir`
                    // as it can be misconfigured sometimes or otherwise add
                    // things that we don't want.
                    let mut opts = git2::RepositoryInitOptions::new();
                    opts.external_template(false);
                    Ok(git2::Repository::init_opts(&path, &opts).with_context(|| {
                        format!("failed to initialize index git repository (in {:?})", path)
                    })?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let repo = self.repo()?;
            let oid = self.index_git_ref.resolve(repo)?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<'_, git2::Tree<'_>>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()));
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe { mem::transmute::<git2::Tree<'_>, git2::Tree<'static>>(tree) };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }

    fn current_version(&self) -> Option<InternedString> {
        if let Some(sha) = self.current_sha.get() {
            return Some(sha);
        }
        let sha = InternedString::new(&self.head().ok()?.to_string());
        self.current_sha.set(Some(sha));
        Some(sha)
    }
}

const LAST_UPDATED_FILE: &str = ".last-updated";

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn prepare(&self) -> CargoResult<()> {
        self.repo()?; // create intermediate dirs and initialize the repo
        Ok(())
    }

    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
        self.config.assert_package_cache_locked(path)
    }

    // `index_version` Is a string representing the version of the file used to construct the cached copy.
    // Older versions of Cargo used the single value of the hash of the HEAD commit as a `index_version`.
    // This is technically correct but a little too conservative. If a new commit is fetched all cached
    // files need to be regenerated even if a particular file was not changed.
    // Cargo now checks the `index_version` in two parts the cache file is considered valid if `index_version`
    // ends with the hash of the HEAD commit OR if it starts with the hash of the file's contents.
    // Cargo writes the cached files with `index_version` = `git_file_hash` + ":" + `git_commit_hash`.
    fn load(
        &mut self,
        _root: &Path,
        path: &Path,
        index_version: Option<&str>,
    ) -> Poll<CargoResult<LoadResponse>> {
        if self.needs_update {
            return Poll::Pending;
        }
        // Check if the cache is valid.
        let git_commit_hash = self.current_version();
        if let (Some(i), Some(c)) = (index_version, git_commit_hash) {
            if i.ends_with(c.as_str()) {
                return Poll::Ready(Ok(LoadResponse::CacheValid));
            }
        }
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        fn load_helper(
            registry: &RemoteRegistry<'_>,
            path: &Path,
            index_version: Option<&str>,
            git_commit_hash: Option<&str>,
        ) -> CargoResult<LoadResponse> {
            let repo = registry.repo()?;
            let tree = registry.tree()?;
            let entry = tree.get_path(path);
            let entry = entry?;
            let git_file_hash = entry.id().to_string();

            if let Some(i) = index_version {
                if i.starts_with(git_file_hash.as_str()) {
                    return Ok(LoadResponse::CacheValid);
                }
            }

            let object = entry.to_object(repo)?;
            let blob = match object.as_blob() {
                Some(blob) => blob,
                None => anyhow::bail!("path `{}` is not a blob in the git repo", path.display()),
            };

            Ok(LoadResponse::Data {
                raw_data: blob.content().to_vec(),
                index_version: git_commit_hash.map(|c| git_file_hash + c),
            })
        }

        match load_helper(&self, path, index_version, git_commit_hash.as_deref()) {
            Ok(result) => Poll::Ready(Ok(result)),
            Err(_) if !self.updated => {
                // If git returns an error and we haven't updated the repo, return
                // pending to allow an update to try again.
                self.needs_update = true;
                Poll::Pending
            }
            Err(e)
                if e.downcast_ref::<git2::Error>()
                    .map(|e| e.code() == git2::ErrorCode::NotFound)
                    .unwrap_or_default() =>
            {
                // The repo has been updated and the file does not exist.
                Poll::Ready(Ok(LoadResponse::NotFound))
            }
            Err(e) => Poll::Ready(Err(e)),
        }
    }

    fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
        debug!("loading config");
        self.prepare()?;
        self.config.assert_package_cache_locked(&self.index_path);
        match self.load(Path::new(""), Path::new("config.json"), None)? {
            Poll::Ready(LoadResponse::Data { raw_data, .. }) => {
                trace!("config loaded");
                Poll::Ready(Ok(Some(serde_json::from_slice(&raw_data)?)))
            }
            Poll::Ready(_) => Poll::Ready(Ok(None)),
            Poll::Pending => Poll::Pending,
        }
    }

    fn block_until_ready(&mut self) -> CargoResult<()> {
        if !self.needs_update {
            return Ok(());
        }

        self.updated = true;
        self.needs_update = false;

        if self.config.offline() {
            return Ok(());
        }
        if self.config.cli_unstable().no_index_update {
            return Ok(());
        }
        // Make sure the index is only updated once per session since it is an
        // expensive operation. This generally only happens when the resolver
        // is run multiple times, such as during `cargo publish`.
        if self.config.updated_sources().contains(&self.source_id) {
            return Ok(());
        }

        debug!("updating the index");

        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        self.config.http()?;

        self.prepare()?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        self.current_sha.set(None);
        let path = self.config.assert_package_cache_locked(&self.index_path);
        self.config
            .shell()
            .status("Updating", self.source_id.display_index())?;

        // Fetch the latest version of our `index_git_ref` into the index
        // checkout.
        let url = self.source_id.url();
        let repo = self.repo.borrow_mut().unwrap();
        git::fetch(repo, url.as_str(), &self.index_git_ref, self.config)
            .with_context(|| format!("failed to fetch `{}`", url))?;
        self.config.updated_sources().insert(self.source_id);

        // Create a dummy file to record the mtime for when we updated the
        // index.
        paths::create(&path.join(LAST_UPDATED_FILE))?;

        Ok(())
    }

    fn invalidate_cache(&mut self) {
        if !self.updated {
            self.needs_update = true;
        }
    }

    fn is_updated(&self) -> bool {
        self.updated
    }

    fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
        let registry_config = loop {
            match self.config()? {
                Poll::Pending => self.block_until_ready()?,
                Poll::Ready(cfg) => break cfg.unwrap(),
            }
        };

        download::download(
            &self.cache_path,
            &self.config,
            pkg,
            checksum,
            registry_config,
        )
    }

    fn finish_download(
        &mut self,
        pkg: PackageId,
        checksum: &str,
        data: &[u8],
    ) -> CargoResult<File> {
        download::finish_download(&self.cache_path, &self.config, pkg, checksum, data)
    }

    fn is_crate_downloaded(&self, pkg: PackageId) -> bool {
        download::is_crate_downloaded(&self.cache_path, &self.config, pkg)
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}
<|endoftext|>


<commit_before>use join_to_string::join;

use ra_syntax::{
    algo::{find_covering_node, find_leaf_at_offset},
    ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
    Direction, SourceFileNode,
    SyntaxKind::{COMMA, WHITESPACE},
    SyntaxNodeRef, TextRange, TextUnit,
};

use crate::{find_node_at_offset, Edit, EditBuilder};

#[derive(Debug)]
pub struct LocalEdit {
    pub edit: Edit,
    pub cursor_position: Option<TextUnit>,
}

pub fn flip_comma<'a>(
    file: &'a SourceFileNode,
    offset: TextUnit,
) -> Option<impl FnOnce() -> LocalEdit + 'a> {
    let syntax = file.syntax();

    let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?;
    let prev = non_trivia_sibling(comma, Direction::Prev)?;
    let next = non_trivia_sibling(comma, Direction::Next)?;
    Some(move || {
        let mut edit = EditBuilder::new();
        edit.replace(prev.range(), next.text().to_string());
        edit.replace(next.range(), prev.text().to_string());
        LocalEdit {
            edit: edit.finish(),
            cursor_position: None,
        }
    })
}

pub fn add_derive<'a>(
    file: &'a SourceFileNode,
    offset: TextUnit,
) -> Option<impl FnOnce() -> LocalEdit + 'a> {
    let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
    Some(move || {
        let derive_attr = nominal
            .attrs()
            .filter_map(|x| x.as_call())
            .filter(|(name, _arg)| name == "derive")
            .map(|(_name, arg)| arg)
            .next();
        let mut edit = EditBuilder::new();
        let offset = match derive_attr {
            None => {
                let node_start = nominal.syntax().range().start();
                edit.insert(node_start, "#[derive()]\n".to_string());
                node_start + TextUnit::of_str("#[derive(")
            }
            Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
        };
        LocalEdit {
            edit: edit.finish(),
            cursor_position: Some(offset),
        }
    })
}

pub fn add_impl<'a>(
    file: &'a SourceFileNode,
    offset: TextUnit,
) -> Option<impl FnOnce() -> LocalEdit + 'a> {
    let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
    let name = nominal.name()?;

    Some(move || {
        let type_params = nominal.type_param_list();
        let mut edit = EditBuilder::new();
        let start_offset = nominal.syntax().range().end();
        let mut buf = String::new();
        buf.push_str("\n\nimpl");
        if let Some(type_params) = type_params {
            type_params.syntax().text().push_to(&mut buf);
        }
        buf.push_str(" ");
        buf.push_str(name.text().as_str());
        if let Some(type_params) = type_params {
            let lifetime_params = type_params
                .lifetime_params()
                .filter_map(|it| it.lifetime())
                .map(|it| it.text());
            let type_params = type_params
                .type_params()
                .filter_map(|it| it.name())
                .map(|it| it.text());
            join(lifetime_params.chain(type_params))
                .surround_with("<", ">")
                .to_buf(&mut buf);
        }
        buf.push_str(" {\n");
        let offset = start_offset + TextUnit::of_str(&buf);
        buf.push_str("\n}");
        edit.insert(start_offset, buf);
        LocalEdit {
            edit: edit.finish(),
            cursor_position: Some(offset),
        }
    })
}

pub fn introduce_variable<'a>(
    file: &'a SourceFileNode,
    range: TextRange,
) -> Option<impl FnOnce() -> LocalEdit + 'a> {
    let node = find_covering_node(file.syntax(), range);
    let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;

    let anchor_stmt = ahchor_stmt(expr)?;
    let indent = anchor_stmt.prev_sibling()?;
    if indent.kind() != WHITESPACE {
        return None;
    }
    return Some(move || {
        let mut buf = String::new();
        let mut edit = EditBuilder::new();

        buf.push_str("let var_name = ");
        expr.syntax().text().push_to(&mut buf);
        if expr.syntax().range().start() == anchor_stmt.range().start() {
            edit.replace(expr.syntax().range(), buf);
        } else {
            buf.push_str(";");
            indent.text().push_to(&mut buf);
            edit.replace(expr.syntax().range(), "var_name".to_string());
            edit.insert(anchor_stmt.range().start(), buf);
        }
        let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let ");
        LocalEdit {
            edit: edit.finish(),
            cursor_position: Some(cursor_position),
        }
    });

    /// Statement or last in the block expression, which will follow
    /// the freshly introduced var.
    fn ahchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> {
        expr.syntax().ancestors().find(|&node| {
            if ast::Stmt::cast(node).is_some() {
                return true;
            }
            if let Some(expr) = node
                .parent()
                .and_then(ast::Block::cast)
                .and_then(|it| it.expr())
            {
                if expr.syntax() == node {
                    return true;
                }
            }
            false
        })
    }
}

fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> {
    node.siblings(direction)
        .skip(1)
        .find(|node| !node.kind().is_trivia())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::{check_action, check_action_range};

    #[test]
    fn test_swap_comma() {
        check_action(
            "fn foo(x: i32,<|> y: Result<(), ()>) {}",
            "fn foo(y: Result<(), ()>,<|> x: i32) {}",
            |file, off| flip_comma(file, off).map(|f| f()),
        )
    }

    #[test]
    fn test_add_derive() {
        check_action(
            "struct Foo { a: i32, <|>}",
            "#[derive(<|>)]\nstruct Foo { a: i32, }",
            |file, off| add_derive(file, off).map(|f| f()),
        );
        check_action(
            "struct Foo { <|> a: i32, }",
            "#[derive(<|>)]\nstruct Foo {  a: i32, }",
            |file, off| add_derive(file, off).map(|f| f()),
        );
        check_action(
            "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
            "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
            |file, off| add_derive(file, off).map(|f| f()),
        );
    }

    #[test]
    fn test_add_impl() {
        check_action(
            "struct Foo {<|>}\n",
            "struct Foo {}\n\nimpl Foo {\n<|>\n}\n",
            |file, off| add_impl(file, off).map(|f| f()),
        );
        check_action(
            "struct Foo<T: Clone> {<|>}",
            "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
            |file, off| add_impl(file, off).map(|f| f()),
        );
        check_action(
            "struct Foo<'a, T: Foo<'a>> {<|>}",
            "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}",
            |file, off| add_impl(file, off).map(|f| f()),
        );
    }

    #[test]
    fn test_intrdoduce_var_simple() {
        check_action_range(
            "
fn foo() {
    foo(<|>1 + 1<|>);
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    foo(var_name);
}",
            |file, range| introduce_variable(file, range).map(|f| f()),
        );
    }

    #[test]
    fn test_intrdoduce_var_expr_stmt() {
        check_action_range(
            "
fn foo() {
    <|>1 + 1<|>;
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
}",
            |file, range| introduce_variable(file, range).map(|f| f()),
        );
    }

    #[test]
    fn test_intrdoduce_var_last_expr() {
        check_action_range(
            "
fn foo() {
    bar(<|>1 + 1<|>)
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    bar(var_name)
}",
            |file, range| introduce_variable(file, range).map(|f| f()),
        );
    }

}
<commit_msg>Add `derive` after doc comments<commit_after>use join_to_string::join;

use ra_syntax::{
    algo::{find_covering_node, find_leaf_at_offset},
    ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
    Direction, SourceFileNode,
    SyntaxKind::{COMMA, WHITESPACE, COMMENT},
    SyntaxNodeRef, TextRange, TextUnit,
};

use crate::{find_node_at_offset, Edit, EditBuilder};

#[derive(Debug)]
pub struct LocalEdit {
    pub edit: Edit,
    pub cursor_position: Option<TextUnit>,
}

pub fn flip_comma<'a>(
    file: &'a SourceFileNode,
    offset: TextUnit,
) -> Option<impl FnOnce() -> LocalEdit + 'a> {
    let syntax = file.syntax();

    let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?;
    let prev = non_trivia_sibling(comma, Direction::Prev)?;
    let next = non_trivia_sibling(comma, Direction::Next)?;
    Some(move || {
        let mut edit = EditBuilder::new();
        edit.replace(prev.range(), next.text().to_string());
        edit.replace(next.range(), prev.text().to_string());
        LocalEdit {
            edit: edit.finish(),
            cursor_position: None,
        }
    })
}

pub fn add_derive<'a>(
    file: &'a SourceFileNode,
    offset: TextUnit,
) -> Option<impl FnOnce() -> LocalEdit + 'a> {
    let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
    let node_start = derive_insertion_offset(nominal)?;
    return Some(move || {
        let derive_attr = nominal
            .attrs()
            .filter_map(|x| x.as_call())
            .filter(|(name, _arg)| name == "derive")
            .map(|(_name, arg)| arg)
            .next();
        let mut edit = EditBuilder::new();
        let offset = match derive_attr {
            None => {
                edit.insert(node_start, "#[derive()]\n".to_string());
                node_start + TextUnit::of_str("#[derive(")
            }
            Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
        };
        LocalEdit {
            edit: edit.finish(),
            cursor_position: Some(offset),
        }
    });

    // Insert `derive` after doc comments.
    fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> {
        let non_ws_child = nominal
            .syntax()
            .children()
            .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
        Some(non_ws_child.range().start())
    }
}

pub fn add_impl<'a>(
    file: &'a SourceFileNode,
    offset: TextUnit,
) -> Option<impl FnOnce() -> LocalEdit + 'a> {
    let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
    let name = nominal.name()?;

    Some(move || {
        let type_params = nominal.type_param_list();
        let mut edit = EditBuilder::new();
        let start_offset = nominal.syntax().range().end();
        let mut buf = String::new();
        buf.push_str("\n\nimpl");
        if let Some(type_params) = type_params {
            type_params.syntax().text().push_to(&mut buf);
        }
        buf.push_str(" ");
        buf.push_str(name.text().as_str());
        if let Some(type_params) = type_params {
            let lifetime_params = type_params
                .lifetime_params()
                .filter_map(|it| it.lifetime())
                .map(|it| it.text());
            let type_params = type_params
                .type_params()
                .filter_map(|it| it.name())
                .map(|it| it.text());
            join(lifetime_params.chain(type_params))
                .surround_with("<", ">")
                .to_buf(&mut buf);
        }
        buf.push_str(" {\n");
        let offset = start_offset + TextUnit::of_str(&buf);
        buf.push_str("\n}");
        edit.insert(start_offset, buf);
        LocalEdit {
            edit: edit.finish(),
            cursor_position: Some(offset),
        }
    })
}

pub fn introduce_variable<'a>(
    file: &'a SourceFileNode,
    range: TextRange,
) -> Option<impl FnOnce() -> LocalEdit + 'a> {
    let node = find_covering_node(file.syntax(), range);
    let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;

    let anchor_stmt = ahchor_stmt(expr)?;
    let indent = anchor_stmt.prev_sibling()?;
    if indent.kind() != WHITESPACE {
        return None;
    }
    return Some(move || {
        let mut buf = String::new();
        let mut edit = EditBuilder::new();

        buf.push_str("let var_name = ");
        expr.syntax().text().push_to(&mut buf);
        if expr.syntax().range().start() == anchor_stmt.range().start() {
            edit.replace(expr.syntax().range(), buf);
        } else {
            buf.push_str(";");
            indent.text().push_to(&mut buf);
            edit.replace(expr.syntax().range(), "var_name".to_string());
            edit.insert(anchor_stmt.range().start(), buf);
        }
        let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let ");
        LocalEdit {
            edit: edit.finish(),
            cursor_position: Some(cursor_position),
        }
    });

    /// Statement or last in the block expression, which will follow
    /// the freshly introduced var.
    fn ahchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> {
        expr.syntax().ancestors().find(|&node| {
            if ast::Stmt::cast(node).is_some() {
                return true;
            }
            if let Some(expr) = node
                .parent()
                .and_then(ast::Block::cast)
                .and_then(|it| it.expr())
            {
                if expr.syntax() == node {
                    return true;
                }
            }
            false
        })
    }
}

fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> {
    node.siblings(direction)
        .skip(1)
        .find(|node| !node.kind().is_trivia())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::{check_action, check_action_range};

    #[test]
    fn test_swap_comma() {
        check_action(
            "fn foo(x: i32,<|> y: Result<(), ()>) {}",
            "fn foo(y: Result<(), ()>,<|> x: i32) {}",
            |file, off| flip_comma(file, off).map(|f| f()),
        )
    }

    #[test]
    fn add_derive_new() {
        check_action(
            "struct Foo { a: i32, <|>}",
            "#[derive(<|>)]\nstruct Foo { a: i32, }",
            |file, off| add_derive(file, off).map(|f| f()),
        );
        check_action(
            "struct Foo { <|> a: i32, }",
            "#[derive(<|>)]\nstruct Foo {  a: i32, }",
            |file, off| add_derive(file, off).map(|f| f()),
        );
    }

    #[test]
    fn add_derive_existing() {
        check_action(
            "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
            "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
            |file, off| add_derive(file, off).map(|f| f()),
        );
    }

    #[test]
    fn add_derive_new_with_doc_comment() {
        check_action(
            "
/// `Foo` is a pretty important struct.
/// It does stuff.
struct Foo { a: i32<|>, }
            ",
            "
/// `Foo` is a pretty important struct.
/// It does stuff.
#[derive(<|>)]
struct Foo { a: i32, }
            ",
            |file, off| add_derive(file, off).map(|f| f()),
        );
    }

    #[test]
    fn test_add_impl() {
        check_action(
            "struct Foo {<|>}\n",
            "struct Foo {}\n\nimpl Foo {\n<|>\n}\n",
            |file, off| add_impl(file, off).map(|f| f()),
        );
        check_action(
            "struct Foo<T: Clone> {<|>}",
            "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
            |file, off| add_impl(file, off).map(|f| f()),
        );
        check_action(
            "struct Foo<'a, T: Foo<'a>> {<|>}",
            "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}",
            |file, off| add_impl(file, off).map(|f| f()),
        );
    }

    #[test]
    fn test_intrdoduce_var_simple() {
        check_action_range(
            "
fn foo() {
    foo(<|>1 + 1<|>);
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    foo(var_name);
}",
            |file, range| introduce_variable(file, range).map(|f| f()),
        );
    }

    #[test]
    fn test_intrdoduce_var_expr_stmt() {
        check_action_range(
            "
fn foo() {
    <|>1 + 1<|>;
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
}",
            |file, range| introduce_variable(file, range).map(|f| f()),
        );
    }

    #[test]
    fn test_intrdoduce_var_last_expr() {
        check_action_range(
            "
fn foo() {
    bar(<|>1 + 1<|>)
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    bar(var_name)
}",
            |file, range| introduce_variable(file, range).map(|f| f()),
        );
    }

}
<|endoftext|>


<commit_before>use crate::database::PROJECT_DIRECTORY;
use salsa::query_group;
use std::{
    fmt::{self, Display, Formatter},
    fs,
    path::PathBuf,
    sync::Arc,
};

#[query_group(InputDbStorage)]
pub trait InputDb: InputWatcher {
    fn get_string_input(&self, input: Input) -> Option<Arc<String>>;
    fn get_input(&self, input: Input) -> Option<Arc<Vec<u8>>>;
    fn get_open_input(&self, input: Input) -> Option<Arc<Vec<u8>>>;
}

fn get_string_input(db: &dyn InputDb, input: Input) -> Option<Arc<String>> {
    let content = get_input(db, input)?;
    String::from_utf8((*content).clone()).ok().map(Arc::new)
}

fn get_input(db: &dyn InputDb, input: Input) -> Option<Arc<Vec<u8>>> {
    if let Some(content) = db.get_open_input(input.clone()) {
        return Some(content);
    };

    match input {
        Input::File(_) | Input::ExternalFile(_) => {
            let path = input.to_path().unwrap();
            match fs::read(path.clone()) {
                Ok(content) => Some(Arc::new(content)),
                Err(error) if matches!(error.kind(), std::io::ErrorKind::NotFound) => None,
                Err(_) => {
                    log::error!("Unexpected error when reading file {:?}.", path);
                    None
                }
            }
        }
        Input::Untitled(_) => None,
    }
}
fn get_open_input(db: &dyn InputDb, input: Input) -> Option<Arc<Vec<u8>>> {
    // The following line of code shouldn't be neccessary, but it is.
    //
    // We call `GetOpenInputQuery.in_db_mut(self).invalidate(input);`
    // in `Database.did_open_input(…)`, `.did_change_input(…)`, and
    // `.did_close_input(…)` which correctly forces Salsa to re-run this query
    // function the next time this input is used. However, even though the
    // return value changes, Salsa doesn't record an updated `changed_at` value
    // in its internal `ActiveQuery` struct. `Runtime.report_untracked_read()`
    // manually sets this to the current revision.
    db.salsa_runtime().report_untracked_read();

    let content = db.get_open_input_raw(&input)?;
    Some(Arc::new(content))
}

pub trait InputWatcher {
    fn get_open_input_raw(&self, input: &Input) -> Option<Vec<u8>>;
}

#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
pub enum Input {
    /// Contains the path components from [PROJECT_DIRECTORY] to the file.
    ///
    /// `.` and `..` are not allowed.
    File(Vec<String>),
    /// A file not belonging to the current project.
    ///
    /// This is temporary and should become obsolete when we support packages.
    ExternalFile(PathBuf),
    Untitled(String),
}

impl From<PathBuf> for Input {
    fn from(path: PathBuf) -> Self {
        let project_dir = PROJECT_DIRECTORY.lock().unwrap().clone().unwrap();
        let path = match fs::canonicalize(&path)
            .expect("Path does not exist or is invalid.")
            .strip_prefix(fs::canonicalize(project_dir).unwrap())
        {
            Ok(path) => path.to_owned(),
            Err(_) => return Input::ExternalFile(path),
        };

        let components = path
            .components()
            .into_iter()
            .map(|it| match it {
                std::path::Component::Prefix(_) => unreachable!(),
                std::path::Component::RootDir => unreachable!(),
                std::path::Component::CurDir => panic!("`.` is not allowed in an input path."),
                std::path::Component::ParentDir => {
                    panic!("`..` is not allowed in an input path.")
                }
                std::path::Component::Normal(it) => {
                    it.to_str().expect("Invalid UTF-8 in path.").to_owned()
                }
            })
            .collect();
        Input::File(components)
    }
}
impl Input {
    pub fn to_path(&self) -> Option<PathBuf> {
        match self {
            Input::File(components) => {
                let mut path = PROJECT_DIRECTORY.lock().unwrap().clone().unwrap();
                for component in components {
                    path.push(component);
                }
                Some(path)
            }
            Input::ExternalFile(path) => Some(path.clone()),
            Input::Untitled(_) => None,
        }
    }
}
impl Display for Input {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            Input::File(components) => {
                write!(f, "~:")?;
                if let Some(component) = components.first() {
                    write!(f, "{component}")?;
                }
                for component in &components[1..] {
                    write!(f, "/{component}")?;
                }
                Ok(())
            }
            Input::ExternalFile(path) => write!(f, "external-file:{}", path.display()),
            Input::Untitled(name) => write!(f, "untitled:{name}"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{
        compiler::{rcst::Rcst, string_to_rcst::StringToRcst},
        database::Database,
    };

    #[test]
    fn on_demand_input_works() {
        let mut db = Database::default();
        let input = Input::File(vec!["foo.rs".to_string()]);

        db.did_open_input(&input, "123".to_string().into_bytes());
        assert_eq!(
            String::from_utf8(db.get_input(input.clone()).unwrap().as_ref().to_owned()).unwrap(),
            "123"
        );
        assert_eq!(
            db.rcst(input.clone()).unwrap().as_ref().to_owned(),
            vec![Rcst::Int {
                value: 12u8.into(),
                string: "123".to_string()
            },],
        );

        db.did_change_input(&input, "456".to_string().into_bytes());
        assert_eq!(
            String::from_utf8(db.get_input(input.clone()).unwrap().as_ref().to_owned()).unwrap(),
            "456"
        );
        assert_eq!(
            db.rcst(input.clone()).unwrap().as_ref().to_owned(),
            vec![Rcst::Int {
                value: 456u16.into(),
                string: "456".to_string()
            }],
        );

        db.did_close_input(&input);
    }
}
<commit_msg>Fix test expectation<commit_after>use crate::database::PROJECT_DIRECTORY;
use salsa::query_group;
use std::{
    fmt::{self, Display, Formatter},
    fs,
    path::PathBuf,
    sync::Arc,
};

#[query_group(InputDbStorage)]
pub trait InputDb: InputWatcher {
    fn get_string_input(&self, input: Input) -> Option<Arc<String>>;
    fn get_input(&self, input: Input) -> Option<Arc<Vec<u8>>>;
    fn get_open_input(&self, input: Input) -> Option<Arc<Vec<u8>>>;
}

fn get_string_input(db: &dyn InputDb, input: Input) -> Option<Arc<String>> {
    let content = get_input(db, input)?;
    String::from_utf8((*content).clone()).ok().map(Arc::new)
}

fn get_input(db: &dyn InputDb, input: Input) -> Option<Arc<Vec<u8>>> {
    if let Some(content) = db.get_open_input(input.clone()) {
        return Some(content);
    };

    match input {
        Input::File(_) | Input::ExternalFile(_) => {
            let path = input.to_path().unwrap();
            match fs::read(path.clone()) {
                Ok(content) => Some(Arc::new(content)),
                Err(error) if matches!(error.kind(), std::io::ErrorKind::NotFound) => None,
                Err(_) => {
                    log::error!("Unexpected error when reading file {:?}.", path);
                    None
                }
            }
        }
        Input::Untitled(_) => None,
    }
}
fn get_open_input(db: &dyn InputDb, input: Input) -> Option<Arc<Vec<u8>>> {
    // The following line of code shouldn't be neccessary, but it is.
    //
    // We call `GetOpenInputQuery.in_db_mut(self).invalidate(input);`
    // in `Database.did_open_input(…)`, `.did_change_input(…)`, and
    // `.did_close_input(…)` which correctly forces Salsa to re-run this query
    // function the next time this input is used. However, even though the
    // return value changes, Salsa doesn't record an updated `changed_at` value
    // in its internal `ActiveQuery` struct. `Runtime.report_untracked_read()`
    // manually sets this to the current revision.
    db.salsa_runtime().report_untracked_read();

    let content = db.get_open_input_raw(&input)?;
    Some(Arc::new(content))
}

pub trait InputWatcher {
    fn get_open_input_raw(&self, input: &Input) -> Option<Vec<u8>>;
}

#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
pub enum Input {
    /// Contains the path components from [PROJECT_DIRECTORY] to the file.
    ///
    /// `.` and `..` are not allowed.
    File(Vec<String>),
    /// A file not belonging to the current project.
    ///
    /// This is temporary and should become obsolete when we support packages.
    ExternalFile(PathBuf),
    Untitled(String),
}

impl From<PathBuf> for Input {
    fn from(path: PathBuf) -> Self {
        let project_dir = PROJECT_DIRECTORY.lock().unwrap().clone().unwrap();
        let path = match fs::canonicalize(&path)
            .expect("Path does not exist or is invalid.")
            .strip_prefix(fs::canonicalize(project_dir).unwrap())
        {
            Ok(path) => path.to_owned(),
            Err(_) => return Input::ExternalFile(path),
        };

        let components = path
            .components()
            .into_iter()
            .map(|it| match it {
                std::path::Component::Prefix(_) => unreachable!(),
                std::path::Component::RootDir => unreachable!(),
                std::path::Component::CurDir => panic!("`.` is not allowed in an input path."),
                std::path::Component::ParentDir => {
                    panic!("`..` is not allowed in an input path.")
                }
                std::path::Component::Normal(it) => {
                    it.to_str().expect("Invalid UTF-8 in path.").to_owned()
                }
            })
            .collect();
        Input::File(components)
    }
}
impl Input {
    pub fn to_path(&self) -> Option<PathBuf> {
        match self {
            Input::File(components) => {
                let mut path = PROJECT_DIRECTORY.lock().unwrap().clone().unwrap();
                for component in components {
                    path.push(component);
                }
                Some(path)
            }
            Input::ExternalFile(path) => Some(path.clone()),
            Input::Untitled(_) => None,
        }
    }
}
impl Display for Input {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            Input::File(components) => {
                write!(f, "~:")?;
                if let Some(component) = components.first() {
                    write!(f, "{component}")?;
                }
                for component in &components[1..] {
                    write!(f, "/{component}")?;
                }
                Ok(())
            }
            Input::ExternalFile(path) => write!(f, "external-file:{}", path.display()),
            Input::Untitled(name) => write!(f, "untitled:{name}"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{
        compiler::{rcst::Rcst, string_to_rcst::StringToRcst},
        database::Database,
    };

    #[test]
    fn on_demand_input_works() {
        let mut db = Database::default();
        let input = Input::File(vec!["foo.rs".to_string()]);

        db.did_open_input(&input, "123".to_string().into_bytes());
        assert_eq!(
            String::from_utf8(db.get_input(input.clone()).unwrap().as_ref().to_owned()).unwrap(),
            "123"
        );
        assert_eq!(
            db.rcst(input.clone()).unwrap().as_ref().to_owned(),
            vec![Rcst::Int {
                value: 123u8.into(),
                string: "123".to_string()
            },],
        );

        db.did_change_input(&input, "456".to_string().into_bytes());
        assert_eq!(
            String::from_utf8(db.get_input(input.clone()).unwrap().as_ref().to_owned()).unwrap(),
            "456"
        );
        assert_eq!(
            db.rcst(input.clone()).unwrap().as_ref().to_owned(),
            vec![Rcst::Int {
                value: 456u16.into(),
                string: "456".to_string()
            }],
        );

        db.did_close_input(&input);
    }
}
<|endoftext|>


<commit_before>use base::{AbstractCFType, AbstractCFTypeRef, Boolean, CFAllocatorRef, CFIndex, CFRelease, CFTypeRef};
use base::{kCFAllocatorDefault, kCFAllocatorNull};
use cast::reinterpret_cast;
use libc::c_char;

pub type UniChar = libc::c_ushort;
pub type CFStringEncoding = u32;

const kCFStringEncodingMacRoman: u32 = 0;
const kCFStringEncodingWindowsLatin1: u32 = 0x0500;
const kCFStringEncodingUTF8: u32 = 0x08000100;

struct __CFString { private: () }
pub type CFStringRef = *__CFString;

impl CFStringRef : AbstractCFTypeRef {
    pure fn as_type_ref(&self) -> CFTypeRef { *self as CFTypeRef }
}

pub struct CFString {
    obj: CFStringRef,

    drop {
        unsafe {
            CFRelease(cast::transmute(self.obj));
        }
    }
}

pub impl CFString : AbstractCFType<CFStringRef> {
    pure fn get_ref() -> CFStringRef { self.obj }

    static fn wrap(obj: CFStringRef) -> CFString {
        CFString { obj: obj }
    }

    static fn unwrap(wrapper: CFString) -> CFStringRef {
        wrapper.obj
    }
}

pub impl CFString {
    // convenience method to make it easier to wrap extern
    // CFStringRefs without providing explicit typarams to base::wrap()
    static fn wrap_extern(string: CFStringRef) -> CFString {
        base::wrap(string)
    }

    static fn new_static(string: &static/str) -> CFString {
        let string_ref = do str::as_buf(string) |bytes, len| {
            CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
                                          bytes,
                                          len as CFIndex,
                                          kCFStringEncodingUTF8,
                                          false as Boolean,
                                          kCFAllocatorNull)
        };
        base::wrap(string_ref)
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFString.h
     */

    // N.B. organized according to "Functions by task" in docs

    /* Creating a CFString */
    //fn CFSTR
    //fn CFStringCreateArrayBySeparatingStrings
    //fn CFStringCreateByCombiningStrings
    //fn CFStringCreateCopy
    //fn CFStringCreateFromExternalRepresentation
    //fn CFStringCreateWithBytes
    fn CFStringCreateWithBytesNoCopy(alloc: CFAllocatorRef,
                                     bytes: *u8,
                                     numBytes: CFIndex,
                                     encoding: CFStringEncoding,
                                     isExternalRepresentation: Boolean,
                                     contentsDeallocator: CFAllocatorRef)
                                  -> CFStringRef;
    //fn CFStringCreateWithCharacters
    //fn CFStringCreateWithCharactersNoCopy
    //fn CFStringCreateWithCString
    //fn CFStringCreateWithCStringNoCopy
    //fn CFStringCreateWithFormat
    //fn CFStringCreateWithFormatAndArguments
    //fn CFStringCreateWithPascalString
    //fn CFStringCreateWithPascalStringNoCopy
    //fn CFStringCreateWithSubstring

    /* Searching Strings */
    //fn CFStringCreateArrayWithFindResults
    //fn CFStringFind
    //fn CFStringFindCharacterFromSet
    //fn CFStringFindWithOptions
    //fn CFStringFindWithOptionsAndLocale
    //fn CFStringGetLineBounds

    /* Comparing Strings */
    //fn CFStringCompare
    //fn CFStringCompareWithOptions
    //fn CFStringCompareWithOptionsAndLocale
    //fn CFStringHasPrefix
    //fn CFStringHasSuffix

    /* Accessing Characters */
    //fn CFStringCreateExternalRepresentation
    //fn CFStringGetBytes
    //fn CFStringGetCharacterAtIndex
    //fn CFStringGetCharacters
    //fn CFStringGetCharactersPtr
    //fn CFStringGetCharacterFromInlineBuffer
    //fn CFStringGetCString
    //fn CFStringGetCStringPtr
    //fn CFStringGetLength
    //fn CFStringGetPascalString
    //fn CFStringGetPascalStringPtr
    //fn CFStringGetRangeOfComposedCharactersAtIndex
    //fn CFStringInitInlineBuffer

    /* Working With Hyphenation */
    //fn CFStringGetHyphenationLocationBeforeIndex
    //fn CFStringIsHyphenationAvailableForLocale

    /* Working With Encodings */
    //fn CFStringConvertEncodingToIANACharSetName
    //fn CFStringConvertEncodingToNSStringEncoding
    //fn CFStringConvertEncodingToWindowsCodepage
    //fn CFStringConvertIANACharSetNameToEncoding
    //fn CFStringConvertNSStringEncodingToEncoding
    //fn CFStringConvertWindowsCodepageToEncoding
    //fn CFStringGetFastestEncoding
    //fn CFStringGetListOfAvailableEncodings
    //fn CFStringGetMaximumSizeForEncoding
    //fn CFStringGetMostCompatibleMacStringEncoding
    //fn CFStringGetNameOfEncoding
    //fn CFStringGetSmallestEncoding
    //fn CFStringGetSystemEncoding
    //fn CFStringIsEncodingAvailable

    /* Getting Numeric Values */
    //fn CFStringGetDoubleValue
    //fn CFStringGetIntValue

    /* Getting String Properties */
    //fn CFShowStr
    //fn CFStringGetTypeID

    /* String File System Representations */
    //fn CFStringCreateWithFileSystemRepresentation
    //fn CFStringGetFileSystemRepresentation
    //fn CFStringGetMaximumSizeOfFileSystemRepresentation

    /* Getting Paragraph Bounds */
    //fn CFStringGetParagraphBounds

    /* Managing Surrogates */
    //fn CFStringGetLongCharacterForSurrogatePair
    //fn CFStringGetSurrogatePairForLongCharacter
    //fn CFStringIsSurrogateHighCharacter
    //fn CFStringIsSurrogateLowCharacter
}

<commit_msg>Add all the CFString constants and enums.<commit_after>use base::{
    AbstractCFType,
    AbstractCFTypeRef,
    Boolean,
    CFAllocatorRef,
    CFIndex,
    CFOptionFlags,
    CFRelease,
    CFTypeRef,

    kCFAllocatorDefault,
    kCFAllocatorNull,
};

use cast::reinterpret_cast;
use libc::c_char;

pub type UniChar = libc::c_ushort;

/*
 * CFString.h
 */
pub type CFStringCompareFlags = CFOptionFlags;
const kCFCompareCaseInsensitive: CFStringCompareFlags = 1;
const kCFCompareBackwards: CFStringCompareFlags = 4;
const kCFCompareAnchored: CFStringCompareFlags = 8;
const kCFCompareNonliteral: CFStringCompareFlags = 16;
const kCFCompareLocalized: CFStringCompareFlags = 32;
const kCFCompareNumerically: CFStringCompareFlags = 64;
const kCFCompareDiacriticInsensitive: CFStringCompareFlags = 128;
const kCFCompareWidthInsensitive: CFStringCompareFlags = 256;
const kCFCompareForcedOrdering: CFStringCompareFlags = 512;

pub type CFStringEncoding = u32;

/* OSX built-in encodings. */
//const kCFStringEncodingMacRoman: CFStringEncoding = 0;
const kCFStringEncodingWindowsLatin1: CFStringEncoding = 0x0500;
const kCFStringEncodingISOLatin1: CFStringEncoding = 0x0201;
const kCFStringEncodingNextStepLatin: CFStringEncoding = 0x0B01;
const kCFStringEncodingASCII: CFStringEncoding = 0x0600;
const kCFStringEncodingUnicode: CFStringEncoding = 0x0100;
const kCFStringEncodingUTF8: CFStringEncoding = 0x08000100;
const kCFStringEncodingNonLossyASCII: CFStringEncoding = 0x0BFF;

const kCFStringEncodingUTF16: CFStringEncoding = 0x0100;
const kCFStringEncodingUTF16BE: CFStringEncoding = 0x10000100;
const kCFStringEncodingUTF16LE: CFStringEncoding = 0x14000100;
const kCFStringEncodingUTF32: CFStringEncoding = 0x0c000100;
const kCFStringEncodingUTF32BE: CFStringEncoding = 0x18000100;
const kCFStringEncodingUTF32LE: CFStringEncoding = 0x1c000100;

/*
 * CFStringEncodingExt.h
 */

type CFStringEncodings = CFIndex;

/* External encodings, except those defined above. */
//const kCFStringEncodingMacRoman: CFStringEncoding = 0;
const kCFStringEncodingMacJapanese: CFStringEncoding = 1;
const kCFStringEncodingMacChineseTrad: CFStringEncoding = 2;
const kCFStringEncodingMacKorean: CFStringEncoding = 3;
const kCFStringEncodingMacArabic: CFStringEncoding = 4;
const kCFStringEncodingMacHebrew: CFStringEncoding = 5;
const kCFStringEncodingMacGreek: CFStringEncoding = 6;
const kCFStringEncodingMacCyrillic: CFStringEncoding = 7;
const kCFStringEncodingMacDevanagari: CFStringEncoding = 9;
const kCFStringEncodingMacGurmukhi: CFStringEncoding = 10;
const kCFStringEncodingMacGujarati: CFStringEncoding = 11;
const kCFStringEncodingMacOriya: CFStringEncoding = 12;
const kCFStringEncodingMacBengali: CFStringEncoding = 13;
const kCFStringEncodingMacTamil: CFStringEncoding = 14;
const kCFStringEncodingMacTelugu: CFStringEncoding = 15;
const kCFStringEncodingMacKannada: CFStringEncoding = 16;
const kCFStringEncodingMacMalayalam: CFStringEncoding = 17;
const kCFStringEncodingMacSinhalese: CFStringEncoding = 18;
const kCFStringEncodingMacBurmese: CFStringEncoding = 19;
const kCFStringEncodingMacKhmer: CFStringEncoding = 20;
const kCFStringEncodingMacThai: CFStringEncoding = 21;
const kCFStringEncodingMacLaotian: CFStringEncoding = 22;
const kCFStringEncodingMacGeorgian: CFStringEncoding = 23;
const kCFStringEncodingMacArmenian: CFStringEncoding = 24;
const kCFStringEncodingMacChineseSimp: CFStringEncoding = 25;
const kCFStringEncodingMacTibetan: CFStringEncoding = 26;
const kCFStringEncodingMacMongolian: CFStringEncoding = 27;
const kCFStringEncodingMacEthiopic: CFStringEncoding = 28;
const kCFStringEncodingMacCentralEurRoman: CFStringEncoding = 29;
const kCFStringEncodingMacVietnamese: CFStringEncoding = 30;
const kCFStringEncodingMacExtArabic: CFStringEncoding = 31;
const kCFStringEncodingMacSymbol: CFStringEncoding = 33;
const kCFStringEncodingMacDingbats: CFStringEncoding = 34;
const kCFStringEncodingMacTurkish: CFStringEncoding = 35;
const kCFStringEncodingMacCroatian: CFStringEncoding = 36;
const kCFStringEncodingMacIcelandic: CFStringEncoding = 37;
const kCFStringEncodingMacRomanian: CFStringEncoding = 38;
const kCFStringEncodingMacCeltic: CFStringEncoding = 39;
const kCFStringEncodingMacGaelic: CFStringEncoding = 40;
const kCFStringEncodingMacFarsi: CFStringEncoding = 0x8C;
const kCFStringEncodingMacUkrainian: CFStringEncoding = 0x98;
const kCFStringEncodingMacInuit: CFStringEncoding = 0xEC;
const kCFStringEncodingMacVT100: CFStringEncoding = 0xFC;
const kCFStringEncodingMacHFS: CFStringEncoding = 0xFF;
//const kCFStringEncodingISOLatin1: CFStringEncoding = 0x0201;
const kCFStringEncodingISOLatin2: CFStringEncoding = 0x0202;
const kCFStringEncodingISOLatin3: CFStringEncoding = 0x0203;
const kCFStringEncodingISOLatin4: CFStringEncoding = 0x0204;
const kCFStringEncodingISOLatinCyrillic: CFStringEncoding = 0x0205;
const kCFStringEncodingISOLatinArabic: CFStringEncoding = 0x0206;
const kCFStringEncodingISOLatinGreek: CFStringEncoding = 0x0207;
const kCFStringEncodingISOLatinHebrew: CFStringEncoding = 0x0208;
const kCFStringEncodingISOLatin5: CFStringEncoding = 0x0209;
const kCFStringEncodingISOLatin6: CFStringEncoding = 0x020A;
const kCFStringEncodingISOLatinThai: CFStringEncoding = 0x020B;
const kCFStringEncodingISOLatin7: CFStringEncoding = 0x020D;
const kCFStringEncodingISOLatin8: CFStringEncoding = 0x020E;
const kCFStringEncodingISOLatin9: CFStringEncoding = 0x020F;
const kCFStringEncodingISOLatin10: CFStringEncoding = 0x0210;
const kCFStringEncodingDOSLatinUS: CFStringEncoding = 0x0400;
const kCFStringEncodingDOSGreek: CFStringEncoding = 0x0405;
const kCFStringEncodingDOSBalticRim: CFStringEncoding = 0x0406;
const kCFStringEncodingDOSLatin1: CFStringEncoding = 0x0410;
const kCFStringEncodingDOSGreek1: CFStringEncoding = 0x0411;
const kCFStringEncodingDOSLatin2: CFStringEncoding = 0x0412;
const kCFStringEncodingDOSCyrillic: CFStringEncoding = 0x0413;
const kCFStringEncodingDOSTurkish: CFStringEncoding = 0x0414;
const kCFStringEncodingDOSPortuguese: CFStringEncoding = 0x0415;
const kCFStringEncodingDOSIcelandic: CFStringEncoding = 0x0416;
const kCFStringEncodingDOSHebrew: CFStringEncoding = 0x0417;
const kCFStringEncodingDOSCanadianFrench: CFStringEncoding = 0x0418;
const kCFStringEncodingDOSArabic: CFStringEncoding = 0x0419;
const kCFStringEncodingDOSNordic: CFStringEncoding = 0x041A;
const kCFStringEncodingDOSRussian: CFStringEncoding = 0x041B;
const kCFStringEncodingDOSGreek2: CFStringEncoding = 0x041C;
const kCFStringEncodingDOSThai: CFStringEncoding = 0x041D;
const kCFStringEncodingDOSJapanese: CFStringEncoding = 0x0420;
const kCFStringEncodingDOSChineseSimplif: CFStringEncoding = 0x0421;
const kCFStringEncodingDOSKorean: CFStringEncoding = 0x0422;
const kCFStringEncodingDOSChineseTrad: CFStringEncoding = 0x0423;
//const kCFStringEncodingWindowsLatin1: CFStringEncoding = 0x0500;
const kCFStringEncodingWindowsLatin2: CFStringEncoding = 0x0501;
const kCFStringEncodingWindowsCyrillic: CFStringEncoding = 0x0502;
const kCFStringEncodingWindowsGreek: CFStringEncoding = 0x0503;
const kCFStringEncodingWindowsLatin5: CFStringEncoding = 0x0504;
const kCFStringEncodingWindowsHebrew: CFStringEncoding = 0x0505;
const kCFStringEncodingWindowsArabic: CFStringEncoding = 0x0506;
const kCFStringEncodingWindowsBalticRim: CFStringEncoding = 0x0507;
const kCFStringEncodingWindowsVietnamese: CFStringEncoding = 0x0508;
const kCFStringEncodingWindowsKoreanJohab: CFStringEncoding = 0x0510;
//const kCFStringEncodingASCII: CFStringEncoding = 0x0600;
const kCFStringEncodingANSEL: CFStringEncoding = 0x0601;
const kCFStringEncodingJIS_X0201_76: CFStringEncoding = 0x0620;
const kCFStringEncodingJIS_X0208_83: CFStringEncoding = 0x0621;
const kCFStringEncodingJIS_X0208_90: CFStringEncoding = 0x0622;
const kCFStringEncodingJIS_X0212_90: CFStringEncoding = 0x0623;
const kCFStringEncodingJIS_C6226_78: CFStringEncoding = 0x0624;
const kCFStringEncodingShiftJIS_X0213: CFStringEncoding = 0x0628;
const kCFStringEncodingShiftJIS_X0213_MenKuTen: CFStringEncoding = 0x0629;
const kCFStringEncodingGB_2312_80: CFStringEncoding = 0x0630;
const kCFStringEncodingGBK_95: CFStringEncoding = 0x0631;
const kCFStringEncodingGB_18030_2000: CFStringEncoding = 0x0632;
const kCFStringEncodingKSC_5601_87: CFStringEncoding = 0x0640;
const kCFStringEncodingKSC_5601_92_Johab: CFStringEncoding = 0x0641;
const kCFStringEncodingCNS_11643_92_P1: CFStringEncoding = 0x0651;
const kCFStringEncodingCNS_11643_92_P2: CFStringEncoding = 0x0652;
const kCFStringEncodingCNS_11643_92_P3: CFStringEncoding = 0x0653;
const kCFStringEncodingISO_2022_JP: CFStringEncoding = 0x0820;
const kCFStringEncodingISO_2022_JP_2: CFStringEncoding = 0x0821;
const kCFStringEncodingISO_2022_JP_1: CFStringEncoding = 0x0822;
const kCFStringEncodingISO_2022_JP_3: CFStringEncoding = 0x0823;
const kCFStringEncodingISO_2022_CN: CFStringEncoding = 0x0830;
const kCFStringEncodingISO_2022_CN_EXT: CFStringEncoding = 0x0831;
const kCFStringEncodingISO_2022_KR: CFStringEncoding = 0x0840;
const kCFStringEncodingEUC_JP: CFStringEncoding = 0x0920;
const kCFStringEncodingEUC_CN: CFStringEncoding = 0x0930;
const kCFStringEncodingEUC_TW: CFStringEncoding = 0x0931;
const kCFStringEncodingEUC_KR: CFStringEncoding = 0x0940;
const kCFStringEncodingShiftJIS: CFStringEncoding = 0x0A01;
const kCFStringEncodingKOI8_R: CFStringEncoding = 0x0A02;
const kCFStringEncodingBig5: CFStringEncoding = 0x0A03;
const kCFStringEncodingMacRomanLatin1: CFStringEncoding = 0x0A04;
const kCFStringEncodingHZ_GB_2312: CFStringEncoding = 0x0A05;
const kCFStringEncodingBig5_HKSCS_1999: CFStringEncoding = 0x0A06;
const kCFStringEncodingVISCII: CFStringEncoding = 0x0A07;
const kCFStringEncodingKOI8_U: CFStringEncoding = 0x0A08;
const kCFStringEncodingBig5_E: CFStringEncoding = 0x0A09;
//const kCFStringEncodingNextStepLatin: CFStringEncoding = 0x0B01;
const kCFStringEncodingNextStepJapanese: CFStringEncoding = 0x0B02;
const kCFStringEncodingEBCDIC_US: CFStringEncoding = 0x0C01;
const kCFStringEncodingEBCDIC_CP037: CFStringEncoding = 0x0C02;
const kCFStringEncodingUTF7: CFStringEncoding = 0x04000100;
const kCFStringEncodingUTF7_IMAP: CFStringEncoding = 0x0A10;
const kCFStringEncodingShiftJIS_X0213_00: CFStringEncoding = 0x0628; /* Deprecated */

struct __CFString { private: () }
pub type CFStringRef = *__CFString;

impl CFStringRef : AbstractCFTypeRef {
    pure fn as_type_ref(&self) -> CFTypeRef { *self as CFTypeRef }
}

pub struct CFString {
    obj: CFStringRef,

    drop {
        unsafe {
            CFRelease(cast::transmute(self.obj));
        }
    }
}

pub impl CFString : AbstractCFType<CFStringRef> {
    pure fn get_ref() -> CFStringRef { self.obj }

    static fn wrap(obj: CFStringRef) -> CFString {
        CFString { obj: obj }
    }

    static fn unwrap(wrapper: CFString) -> CFStringRef {
        wrapper.obj
    }
}

pub impl CFString {
    // convenience method to make it easier to wrap extern
    // CFStringRefs without providing explicit typarams to base::wrap()
    static fn wrap_extern(string: CFStringRef) -> CFString {
        base::wrap(string)
    }

    static fn new_static(string: &static/str) -> CFString {
        let string_ref = do str::as_buf(string) |bytes, len| {
            CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
                                          bytes,
                                          len as CFIndex,
                                          kCFStringEncodingUTF8,
                                          false as Boolean,
                                          kCFAllocatorNull)
        };
        base::wrap(string_ref)
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFString.h
     */

    // N.B. organized according to "Functions by task" in docs

    /* Creating a CFString */
    //fn CFSTR
    //fn CFStringCreateArrayBySeparatingStrings
    //fn CFStringCreateByCombiningStrings
    //fn CFStringCreateCopy
    //fn CFStringCreateFromExternalRepresentation
    //fn CFStringCreateWithBytes
    fn CFStringCreateWithBytesNoCopy(alloc: CFAllocatorRef,
                                     bytes: *u8,
                                     numBytes: CFIndex,
                                     encoding: CFStringEncoding,
                                     isExternalRepresentation: Boolean,
                                     contentsDeallocator: CFAllocatorRef)
                                  -> CFStringRef;
    //fn CFStringCreateWithCharacters
    //fn CFStringCreateWithCharactersNoCopy
    //fn CFStringCreateWithCString
    //fn CFStringCreateWithCStringNoCopy
    //fn CFStringCreateWithFormat
    //fn CFStringCreateWithFormatAndArguments
    //fn CFStringCreateWithPascalString
    //fn CFStringCreateWithPascalStringNoCopy
    //fn CFStringCreateWithSubstring

    /* Searching Strings */
    //fn CFStringCreateArrayWithFindResults
    //fn CFStringFind
    //fn CFStringFindCharacterFromSet
    //fn CFStringFindWithOptions
    //fn CFStringFindWithOptionsAndLocale
    //fn CFStringGetLineBounds

    /* Comparing Strings */
    //fn CFStringCompare
    //fn CFStringCompareWithOptions
    //fn CFStringCompareWithOptionsAndLocale
    //fn CFStringHasPrefix
    //fn CFStringHasSuffix

    /* Accessing Characters */
    //fn CFStringCreateExternalRepresentation
    //fn CFStringGetBytes
    //fn CFStringGetCharacterAtIndex
    //fn CFStringGetCharacters
    //fn CFStringGetCharactersPtr
    //fn CFStringGetCharacterFromInlineBuffer
    //fn CFStringGetCString
    //fn CFStringGetCStringPtr
    //fn CFStringGetLength
    //fn CFStringGetPascalString
    //fn CFStringGetPascalStringPtr
    //fn CFStringGetRangeOfComposedCharactersAtIndex
    //fn CFStringInitInlineBuffer

    /* Working With Hyphenation */
    //fn CFStringGetHyphenationLocationBeforeIndex
    //fn CFStringIsHyphenationAvailableForLocale

    /* Working With Encodings */
    //fn CFStringConvertEncodingToIANACharSetName
    //fn CFStringConvertEncodingToNSStringEncoding
    //fn CFStringConvertEncodingToWindowsCodepage
    //fn CFStringConvertIANACharSetNameToEncoding
    //fn CFStringConvertNSStringEncodingToEncoding
    //fn CFStringConvertWindowsCodepageToEncoding
    //fn CFStringGetFastestEncoding
    //fn CFStringGetListOfAvailableEncodings
    //fn CFStringGetMaximumSizeForEncoding
    //fn CFStringGetMostCompatibleMacStringEncoding
    //fn CFStringGetNameOfEncoding
    //fn CFStringGetSmallestEncoding
    //fn CFStringGetSystemEncoding
    //fn CFStringIsEncodingAvailable

    /* Getting Numeric Values */
    //fn CFStringGetDoubleValue
    //fn CFStringGetIntValue

    /* Getting String Properties */
    //fn CFShowStr
    //fn CFStringGetTypeID

    /* String File System Representations */
    //fn CFStringCreateWithFileSystemRepresentation
    //fn CFStringGetFileSystemRepresentation
    //fn CFStringGetMaximumSizeOfFileSystemRepresentation

    /* Getting Paragraph Bounds */
    //fn CFStringGetParagraphBounds

    /* Managing Surrogates */
    //fn CFStringGetLongCharacterForSurrogatePair
    //fn CFStringGetSurrogatePairForLongCharacter
    //fn CFStringIsSurrogateHighCharacter
    //fn CFStringIsSurrogateLowCharacter
}

<|endoftext|>