<commit_before>extern crate rustc_serialize;
extern crate tantivy;
extern crate tempdir;

use std::path::Path;
use tempdir::TempDir;
use tantivy::Index;
use tantivy::schema::*;
use tantivy::collector::TopCollector;
use tantivy::query::QueryParser;

fn main() {
    // Let's create a temporary directory for the
    // sake of this example
    if let Ok(dir) = TempDir::new("tantivy_example_dir") {
        run_example(dir.path()).unwrap();
        dir.close().unwrap();
    }
}

fn run_example(index_path: &Path) -> tantivy::Result<()> {

    // # Defining the schema
    //
    // The Tantivy index requires a very strict schema.
    // The schema declares which fields are in the index,
    // and for each field, its type and "the way it should
    // be indexed".

    // first we need to define a schema ...
    let mut schema_builder = SchemaBuilder::default();

    // Our first field is title.
    // We want full-text search for it, and we want to be able
    // to retrieve the document after the search.
    //
    // TEXT | STORED is some syntactic sugar to describe
    // that.
    //
    // `TEXT` means the field should be tokenized and indexed,
    // along with its term frequency and term positions.
    //
    // `STORED` means that the field will also be saved
    // in a compressed, row-oriented key-value store.
    // This store is useful to reconstruct the
    // documents that were selected during the search phase.
    schema_builder.add_text_field("title", TEXT | STORED);

    // Our first field is body.
    // We want full-text search for it, and we want to be able
    // to retrieve the body after the search.
    schema_builder.add_text_field("body", TEXT);

    let schema = schema_builder.build();

    // # Indexing documents
    //
    // Let's create a brand new index.
    //
    // This will actually just save a meta.json
    // with our schema in the directory.
    let index = try!(Index::create(index_path, schema.clone()));

    // To insert document we need an index writer.
    // There must be only one writer at a time.
    // This single `IndexWriter` is already
    // multithreaded.
    //
    // Here we use a buffer of 50MB. Using a bigger
    // heap for the indexer can increase its throughput.
    // This buffer will be split between the indexing
    // threads.
    let mut index_writer = try!(index.writer(50_000_000));

    // Let's index our documents!
    // We first need a handle on the title and the body field.

    // ### Create a document "manually".
    //
    // We can create a document manually, by setting the fields
    // one by one in a Document object.
    let title = schema.get_field("title").unwrap();
    let body = schema.get_field("body").unwrap();

    let mut old_man_doc = Document::default();
    old_man_doc.add_text(title, "The Old Man and the Sea");
    old_man_doc.add_text(body,
                         "He was an old man who fished alone in a skiff in the Gulf Stream and \
                          he had gone eighty-four days now without taking a fish.");

    // ... and add it to the `IndexWriter`.
    index_writer.add_document(old_man_doc);

    // ### Create a document directly from json.
    //
    // Alternatively, we can use our schema to parse
    // a document object directly from json.

    let mice_and_men_doc = try!(schema.parse_document(r#"{
       "title": "Of Mice and Men",
       "body": "few miles south of Soledad, the Salinas River drops in close to the hillside bank and runs deep and green. The water is warm too, for it has slipped twinkling over the yellow sands in the sunlight before reaching the narrow pool. On one side of the river the golden foothill slopes curve up to the strong and rocky Gabilan Mountains, but on the valley side the water is lined with trees—willows fresh and green with every spring, carrying in their lower leaf junctures the debris of the winter’s flooding; and sycamores with mottled, white,recumbent limbs and branches that arch over the pool"
    }"#));

    index_writer.add_document(mice_and_men_doc);

    // Multi-valued field are allowed, they are
    // expressed in JSON by an array.
    // The following document has two titles.
    let frankenstein_doc = try!(schema.parse_document(r#"{
       "title": ["Frankenstein", "The Modern Promotheus"],
       "body": "You will rejoice to hear that no disaster has accompanied the commencement of an enterprise which you have regarded with such evil forebodings.  I arrived here yesterday, and my first task is to assure my dear sister of my welfare and increasing confidence in the success of my undertaking."
    }"#));
    index_writer.add_document(frankenstein_doc);

    // This is an example, so we will only index 3 documents
    // here. You can check out tantivy's tutorial to index
    // the English wikipedia. Tantivy's indexing is rather fast.
    // Indexing 5 million articles of the English wikipedia takes
    // around 4 minutes on my computer!

    // ### Committing
    //
    // At this point our documents are not searchable.
    //
    //
    // We need to call .commit() explicitly to force the
    // index_writer to finish processing the documents in the queue,
    // flush the current index to the disk, and advertise
    // the existence of new documents.
    //
    // This call is blocking.
    try!(index_writer.commit());

    // If `.commit()` returns correctly, then all of the
    // documents that have been added are guaranteed to be
    // persistently indexed.
    //
    // In the scenario of a crash or a power failure,
    // tantivy behaves as if has rolled back to its last
    // commit.

    // # Searching
    //
    // Let's search our index. Start by reloading
    // searchers in the index. This should be done
    // after every commit().
    try!(index.load_searchers());

    // Afterwards create one (or more) searchers.
    //
    // You should create a searcher
    // every time you start a "search query".
    let searcher = index.searcher();

    // The query parser can interpret human queries.
    // Here, if the user does not specify which
    // field they want to search, tantivy will search
    // in both title and body.
    let query_parser = QueryParser::new(index.schema(), vec![title, body]);

    // QueryParser may fail if the query is not in the right
    // format. For user facing applications, this can be a problem.
    // A ticket has been opened regarding this problem.
    let query = try!(query_parser.parse_query("sea whale"));

    // A query defines a set of documents, as
    // well as the way they should be scored.
    //
    // A query created by the query parser is scored according
    // to a metric called Tf-Idf, and will consider
    // any document matching at least one of our terms.

    // ### Collectors
    //
    // We are not interested in all of the documents but
    // only in the top 10. Keeping track of our top 10 best documents
    // is the role of the TopCollector.
    let mut top_collector = TopCollector::with_limit(10);

    // We can now perform our query.
    try!(searcher.search(&*query, &mut top_collector));

    // Our top collector now contains the 10
    // most relevant doc ids...
    let doc_addresses = top_collector.docs();

    // The actual documents still need to be
    // retrieved from Tantivy's store.
    //
    // Since the body field was not configured as stored,
    // the document returned will only contain
    // a title.

    for doc_address in doc_addresses {
        let retrieved_doc = try!(searcher.doc(&doc_address));
        println!("{}", schema.to_json(&retrieved_doc));
    }

    Ok(())
}
<commit_msg>fixup<commit_after>extern crate rustc_serialize;
extern crate tantivy;
extern crate tempdir;

use std::path::Path;
use tempdir::TempDir;
use tantivy::Index;
use tantivy::schema::*;
use tantivy::collector::TopCollector;
use tantivy::query::QueryParser;

fn main() {
    // Let's create a temporary directory for the
    // sake of this example
    if let Ok(dir) = TempDir::new("tantivy_example_dir") {
        run_example(dir.path()).unwrap();
        dir.close().unwrap();
    }
}

fn run_example(index_path: &Path) -> tantivy::Result<()> {

    // # Defining the schema
    //
    // The Tantivy index requires a very strict schema.
    // The schema declares which fields are in the index,
    // and for each field, its type and "the way it should
    // be indexed".

    // first we need to define a schema ...
    let mut schema_builder = SchemaBuilder::default();

    // Our first field is title.
    // We want full-text search for it, and we want to be able
    // to retrieve the document after the search.
    //
    // TEXT | STORED is some syntactic sugar to describe
    // that.
    //
    // `TEXT` means the field should be tokenized and indexed,
    // along with its term frequency and term positions.
    //
    // `STORED` means that the field will also be saved
    // in a compressed, row-oriented key-value store.
    // This store is useful to reconstruct the
    // documents that were selected during the search phase.
    schema_builder.add_text_field("title", TEXT | STORED);

    // Our first field is body.
    // We want full-text search for it, and we want to be able
    // to retrieve the body after the search.
    schema_builder.add_text_field("body", TEXT);

    let schema = schema_builder.build();

    // # Indexing documents
    //
    // Let's create a brand new index.
    //
    // This will actually just save a meta.json
    // with our schema in the directory.
    let index = try!(Index::create(index_path, schema.clone()));

    // To insert document we need an index writer.
    // There must be only one writer at a time.
    // This single `IndexWriter` is already
    // multithreaded.
    //
    // Here we use a buffer of 50MB per thread. Using a bigger
    // heap for the indexer can increase its throughput.
    let mut index_writer = try!(index.writer(50_000_000));

    // Let's index our documents!
    // We first need a handle on the title and the body field.

    // ### Create a document "manually".
    //
    // We can create a document manually, by setting the fields
    // one by one in a Document object.
    let title = schema.get_field("title").unwrap();
    let body = schema.get_field("body").unwrap();

    let mut old_man_doc = Document::default();
    old_man_doc.add_text(title, "The Old Man and the Sea");
    old_man_doc.add_text(body,
                         "He was an old man who fished alone in a skiff in the Gulf Stream and \
                          he had gone eighty-four days now without taking a fish.");

    // ... and add it to the `IndexWriter`.
    index_writer.add_document(old_man_doc);

    // ### Create a document directly from json.
    //
    // Alternatively, we can use our schema to parse
    // a document object directly from json.

    let mice_and_men_doc = try!(schema.parse_document(r#"{
       "title": "Of Mice and Men",
       "body": "few miles south of Soledad, the Salinas River drops in close to the hillside bank and runs deep and green. The water is warm too, for it has slipped twinkling over the yellow sands in the sunlight before reaching the narrow pool. On one side of the river the golden foothill slopes curve up to the strong and rocky Gabilan Mountains, but on the valley side the water is lined with trees—willows fresh and green with every spring, carrying in their lower leaf junctures the debris of the winter’s flooding; and sycamores with mottled, white,recumbent limbs and branches that arch over the pool"
    }"#));

    index_writer.add_document(mice_and_men_doc);

    // Multi-valued field are allowed, they are
    // expressed in JSON by an array.
    // The following document has two titles.
    let frankenstein_doc = try!(schema.parse_document(r#"{
       "title": ["Frankenstein", "The Modern Promotheus"],
       "body": "You will rejoice to hear that no disaster has accompanied the commencement of an enterprise which you have regarded with such evil forebodings.  I arrived here yesterday, and my first task is to assure my dear sister of my welfare and increasing confidence in the success of my undertaking."
    }"#));
    index_writer.add_document(frankenstein_doc);

    // This is an example, so we will only index 3 documents
    // here. You can check out tantivy's tutorial to index
    // the English wikipedia. Tantivy's indexing is rather fast.
    // Indexing 5 million articles of the English wikipedia takes
    // around 4 minutes on my computer!

    // ### Committing
    //
    // At this point our documents are not searchable.
    //
    //
    // We need to call .commit() explicitly to force the
    // index_writer to finish processing the documents in the queue,
    // flush the current index to the disk, and advertise
    // the existence of new documents.
    //
    // This call is blocking.
    try!(index_writer.commit());

    // If `.commit()` returns correctly, then all of the
    // documents that have been added are guaranteed to be
    // persistently indexed.
    //
    // In the scenario of a crash or a power failure,
    // tantivy behaves as if has rolled back to its last
    // commit.

    // # Searching
    //
    // Let's search our index. Start by reloading
    // searchers in the index. This should be done
    // after every commit().
    try!(index.load_searchers());

    // Afterwards create one (or more) searchers.
    //
    // You should create a searcher
    // every time you start a "search query".
    let searcher = index.searcher();

    // The query parser can interpret human queries.
    // Here, if the user does not specify which
    // field they want to search, tantivy will search
    // in both title and body.
    let query_parser = QueryParser::new(index.schema(), vec![title, body]);

    // QueryParser may fail if the query is not in the right
    // format. For user facing applications, this can be a problem.
    // A ticket has been opened regarding this problem.
    let query = try!(query_parser.parse_query("sea whale"));

    // A query defines a set of documents, as
    // well as the way they should be scored.
    //
    // A query created by the query parser is scored according
    // to a metric called Tf-Idf, and will consider
    // any document matching at least one of our terms.

    // ### Collectors
    //
    // We are not interested in all of the documents but
    // only in the top 10. Keeping track of our top 10 best documents
    // is the role of the TopCollector.
    let mut top_collector = TopCollector::with_limit(10);

    // We can now perform our query.
    try!(searcher.search(&*query, &mut top_collector));

    // Our top collector now contains the 10
    // most relevant doc ids...
    let doc_addresses = top_collector.docs();

    // The actual documents still need to be
    // retrieved from Tantivy's store.
    //
    // Since the body field was not configured as stored,
    // the document returned will only contain
    // a title.

    for doc_address in doc_addresses {
        let retrieved_doc = try!(searcher.doc(&doc_address));
        println!("{}", schema.to_json(&retrieved_doc));
    }

    Ok(())
}
<|endoftext|>


<commit_before>extern crate polydraw;

use polydraw::node::{Data, NodeBuilder};
use polydraw::lang::{parse, compile, tokenize};

pub const SOURCE: &'static str = "

double = frame * 2

result = <80 60> + double

";

fn main() {
   match tokenize(SOURCE) {
      Ok(tokens) => {
         match parse(tokens) {
            Ok(ast_list) => {
               let mut builder = NodeBuilder::new();

               let frame_index = builder.input(String::from("frame"));

               compile(&mut builder, ast_list);

               let mut program = builder.compile();

               program.input(frame_index, Data::Int(100));

               let result = program.execute();

               println!("{:?}", result);
            },
            Err(err) => println!("{}", err),
         }
      },
      Err(err) => println!("{}", err)
   }
}

<commit_msg>The lang example executing from a source file<commit_after>extern crate polydraw;

use std::env;
use std::io::prelude::*;
use std::fs::File;

use polydraw::node::{Data, NodeBuilder};
use polydraw::lang::{parse, compile, tokenize};

fn main() {
   let filename = match env::args().nth(1) {
      Some(filename) => filename,
      None => {
         println!("No source file specified");
         return;
      }
   };

   let mut f = match File::open(&filename) {
      Ok(f) => f,
      Err(_) => {
         println!("Cannot open {}", &filename);
         return;
      }
   };

   let mut source = String::new();

   f.read_to_string(&mut source).unwrap();

   match tokenize(&source) {
      Ok(tokens) => {
         match parse(tokens) {
            Ok(ast_list) => {
               let mut builder = NodeBuilder::new();

               let frame_index = builder.input(String::from("frame"));

               compile(&mut builder, ast_list);

               let mut program = builder.compile();

               program.input(frame_index, Data::Bool(false));

               let result = program.execute();

               println!(">> {:?}", result);
            },
            Err(err) => println!("{}", err),
         }
      },
      Err(err) => println!("{}", err)
   }
}

<|endoftext|>


<commit_before>use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;
use std::process;

use ::protoc::Protoc;

use crate::gen_and_write::gen_and_write;
use crate::Customize;
mod protoc;
mod pure;

#[derive(Debug)]
enum WhichParser {
    Pure,
    Protoc,
}

impl Default for WhichParser {
    fn default() -> WhichParser {
        WhichParser::Protoc
    }
}

/// Entry point for `.proto` to `.rs` code generation.
///
/// This is similar to `protoc --rust_out...`.
#[derive(Debug, Default)]
pub struct Codegen {
    /// What parser to use to parse `.proto` files.
    which_parser: WhichParser,
    /// --lang_out= param
    out_dir: PathBuf,
    /// -I args
    includes: Vec<PathBuf>,
    /// List of .proto files to compile
    inputs: Vec<PathBuf>,
    /// Customize code generation
    customize: Customize,
    /// Protoc command path
    protoc: Option<Protoc>,
    /// Extra `protoc` args
    extra_args: Vec<OsString>,
}

impl Codegen {
    /// Create new codegen object.
    ///
    /// Uses `protoc` from `$PATH` by default.
    ///
    /// Can be switched to pure rust parser using [`pure`](Self::pure) function.
    pub fn new() -> Self {
        Self::default()
    }

    /// Switch to pure Rust parser of `.proto` files.
    pub fn pure(&mut self) -> &mut Self {
        self.which_parser = WhichParser::Pure;
        self
    }

    /// Switch to `protoc` parser of `.proto` files.
    pub fn protoc(&mut self) -> &mut Self {
        self.which_parser = WhichParser::Protoc;
        self
    }

    /// Output directory for generated code.
    pub fn out_dir(&mut self, out_dir: impl AsRef<Path>) -> &mut Self {
        self.out_dir = out_dir.as_ref().to_owned();
        self
    }

    /// Add an include directory.
    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
        self.includes.push(include.as_ref().to_owned());
        self
    }

    /// Add include directories.
    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for include in includes {
            self.include(include);
        }
        self
    }

    /// Append a `.proto` file path to compile
    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
        self.inputs.push(input.as_ref().to_owned());
        self
    }

    /// Append multiple `.proto` file paths to compile
    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for input in inputs {
            self.input(input);
        }
        self
    }

    /// Specify `protoc` command path to be used when invoking code generation.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # mod protoc_bin_vendored {
    /// #   pub fn protoc_bin_path() -> Result<std::path::PathBuf, std::io::Error> {
    /// #       unimplemented!()
    /// #   }
    /// # }
    ///
    /// use protobuf_codegen::Codegen;
    ///
    /// Codegen::new()
    ///     .protoc()
    ///     .protoc_path(protoc_bin_vendored::protoc_bin_path().unwrap())
    ///     // ...
    ///     .run()
    ///     .unwrap();
    /// ```
    ///
    /// This option is ignored when pure Rust parser is used.
    pub fn protoc_path(&mut self, protoc: impl Into<PathBuf>) -> &mut Self {
        self.protoc = Some(Protoc::from_path(&protoc.into()));
        self
    }

    /// Set options to customize code generation
    pub fn customize(&mut self, customize: Customize) -> &mut Self {
        self.customize = customize;
        self
    }

    /// Extra command line flags for `protoc` invocation.
    ///
    /// For example, `--experimental_allow_proto3_optional` option.
    ///
    /// This option is ignored when pure Rust parser is used.
    pub fn extra_arg(&mut self, arg: impl Into<OsString>) -> &mut Self {
        self.extra_args.push(arg.into());
        self
    }

    /// Invoke the code generation.
    ///
    /// This is roughly equivalent to `protoc --rust_out=...` but
    /// without requiring `protoc-gen-rust` command in `$PATH`.
    ///
    /// This function uses pure Rust parser or `protoc` parser depending on
    /// how this object was configured.
    pub fn run(&self) -> anyhow::Result<()> {
        let (parsed_and_typechecked, parser) = match self.which_parser {
            WhichParser::Protoc => protoc::parse_and_typecheck(self)?,
            WhichParser::Pure => pure::parse_and_typecheck(self)?,
        };
        gen_and_write(
            &parsed_and_typechecked.file_descriptors,
            &parser,
            &parsed_and_typechecked.relative_paths,
            &self.out_dir,
            &self.customize,
        )
    }

    /// Similar to `run`, but prints the message to stderr and exits the process on error.
    pub fn run_from_script(&self) {
        if let Err(e) = self.run() {
            eprintln!("protoc-based codegen failed: {}", e);
            process::exit(1);
        }
    }
}

fn remove_path_prefix<'a>(mut path: &'a Path, mut prefix: &Path) -> Option<&'a Path> {
    path = path.strip_prefix(".").unwrap_or(path);
    prefix = prefix.strip_prefix(".").unwrap_or(prefix);
    path.strip_prefix(prefix).ok()
}

#[test]
fn test_remove_path_prefix() {
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("xxx"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("xxx/"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("../xxx/abc.proto"), Path::new("../xxx/"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("abc.proto"), Path::new("."))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("abc.proto"), Path::new("./"))
    );
    assert_eq!(
        None,
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("yyy"))
    );
    assert_eq!(
        None,
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("yyy/"))
    );
}
<commit_msg>Switch default .proto parser in protobuf-codegen to pure<commit_after>use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;
use std::process;

use ::protoc::Protoc;

use crate::gen_and_write::gen_and_write;
use crate::Customize;
mod protoc;
mod pure;

#[derive(Debug)]
enum WhichParser {
    Pure,
    Protoc,
}

impl Default for WhichParser {
    fn default() -> WhichParser {
        WhichParser::Pure
    }
}

/// Entry point for `.proto` to `.rs` code generation.
///
/// This is similar to `protoc --rust_out...`.
#[derive(Debug, Default)]
pub struct Codegen {
    /// What parser to use to parse `.proto` files.
    which_parser: WhichParser,
    /// --lang_out= param
    out_dir: PathBuf,
    /// -I args
    includes: Vec<PathBuf>,
    /// List of .proto files to compile
    inputs: Vec<PathBuf>,
    /// Customize code generation
    customize: Customize,
    /// Protoc command path
    protoc: Option<Protoc>,
    /// Extra `protoc` args
    extra_args: Vec<OsString>,
}

impl Codegen {
    /// Create new codegen object.
    ///
    /// Uses `protoc` from `$PATH` by default.
    ///
    /// Can be switched to pure rust parser using [`pure`](Self::pure) function.
    pub fn new() -> Self {
        Self::default()
    }

    /// Switch to pure Rust parser of `.proto` files.
    pub fn pure(&mut self) -> &mut Self {
        self.which_parser = WhichParser::Pure;
        self
    }

    /// Switch to `protoc` parser of `.proto` files.
    pub fn protoc(&mut self) -> &mut Self {
        self.which_parser = WhichParser::Protoc;
        self
    }

    /// Output directory for generated code.
    pub fn out_dir(&mut self, out_dir: impl AsRef<Path>) -> &mut Self {
        self.out_dir = out_dir.as_ref().to_owned();
        self
    }

    /// Add an include directory.
    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
        self.includes.push(include.as_ref().to_owned());
        self
    }

    /// Add include directories.
    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for include in includes {
            self.include(include);
        }
        self
    }

    /// Append a `.proto` file path to compile
    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
        self.inputs.push(input.as_ref().to_owned());
        self
    }

    /// Append multiple `.proto` file paths to compile
    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for input in inputs {
            self.input(input);
        }
        self
    }

    /// Specify `protoc` command path to be used when invoking code generation.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # mod protoc_bin_vendored {
    /// #   pub fn protoc_bin_path() -> Result<std::path::PathBuf, std::io::Error> {
    /// #       unimplemented!()
    /// #   }
    /// # }
    ///
    /// use protobuf_codegen::Codegen;
    ///
    /// Codegen::new()
    ///     .protoc()
    ///     .protoc_path(protoc_bin_vendored::protoc_bin_path().unwrap())
    ///     // ...
    ///     .run()
    ///     .unwrap();
    /// ```
    ///
    /// This option is ignored when pure Rust parser is used.
    pub fn protoc_path(&mut self, protoc: impl Into<PathBuf>) -> &mut Self {
        self.protoc = Some(Protoc::from_path(&protoc.into()));
        self
    }

    /// Set options to customize code generation
    pub fn customize(&mut self, customize: Customize) -> &mut Self {
        self.customize = customize;
        self
    }

    /// Extra command line flags for `protoc` invocation.
    ///
    /// For example, `--experimental_allow_proto3_optional` option.
    ///
    /// This option is ignored when pure Rust parser is used.
    pub fn extra_arg(&mut self, arg: impl Into<OsString>) -> &mut Self {
        self.extra_args.push(arg.into());
        self
    }

    /// Invoke the code generation.
    ///
    /// This is roughly equivalent to `protoc --rust_out=...` but
    /// without requiring `protoc-gen-rust` command in `$PATH`.
    ///
    /// This function uses pure Rust parser or `protoc` parser depending on
    /// how this object was configured.
    pub fn run(&self) -> anyhow::Result<()> {
        let (parsed_and_typechecked, parser) = match self.which_parser {
            WhichParser::Protoc => protoc::parse_and_typecheck(self)?,
            WhichParser::Pure => pure::parse_and_typecheck(self)?,
        };
        gen_and_write(
            &parsed_and_typechecked.file_descriptors,
            &parser,
            &parsed_and_typechecked.relative_paths,
            &self.out_dir,
            &self.customize,
        )
    }

    /// Similar to `run`, but prints the message to stderr and exits the process on error.
    pub fn run_from_script(&self) {
        if let Err(e) = self.run() {
            eprintln!("protoc-based codegen failed: {}", e);
            process::exit(1);
        }
    }
}

fn remove_path_prefix<'a>(mut path: &'a Path, mut prefix: &Path) -> Option<&'a Path> {
    path = path.strip_prefix(".").unwrap_or(path);
    prefix = prefix.strip_prefix(".").unwrap_or(prefix);
    path.strip_prefix(prefix).ok()
}

#[test]
fn test_remove_path_prefix() {
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("xxx"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("xxx/"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("../xxx/abc.proto"), Path::new("../xxx/"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("abc.proto"), Path::new("."))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("abc.proto"), Path::new("./"))
    );
    assert_eq!(
        None,
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("yyy"))
    );
    assert_eq!(
        None,
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("yyy/"))
    );
}
<|endoftext|>


<commit_before>#[crate_id(name="wc", vers="1.0.0", author="Boden Garman")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Boden Garman <bpgarman@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::{print, stdin, stderr, File, result, BufferedReader};
use std::str::from_utf8_opt;
use extra::getopts::{groups, Matches};

struct Result {
    filename: ~str,
    bytes: uint,
    chars: uint,
    lines: uint,
    words: uint,
    max_line_length: uint,
}

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        groups::optflag("c", "bytes", "print the byte counts"),
        groups::optflag("m", "chars", "print the character counts"),
        groups::optflag("l", "lines", "print the newline counts"),
        groups::optflag("L", "max-line-length", "print the length of the longest line"),
        groups::optflag("w", "words", "print the word counts"),
        groups::optflag("h", "help", "display this help and exit"),
        groups::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                   "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println!("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println!("");
        print(groups::usage("Print newline, word and byte counts for each FILE", opts));
        println!("");
        println!("With no FILE, or when FILE is -, read standard input.");
        return;
    }

    if (matches.opt_present("version")) {
        println!("wc 1.0.0");
        return;
    }

    let mut files = matches.free.clone();
    if files.is_empty() {
        files = ~[~"-"];
    }

    wc(files, &matches);
}

static CR: u8 = '\r' as u8;
static LF: u8 = '\n' as u8;
static SPACE: u8 = ' ' as u8;
static TAB: u8 = '\t' as u8;
static SYN: u8 = 0x16 as u8;
static FF: u8 = 0x0C as u8;

fn is_word_seperator(byte: u8) -> bool {
    byte == SPACE || byte == TAB || byte == CR || byte == SYN || byte == FF
}

pub fn wc(files: ~[~str], matches: &Matches) {
    let mut total_line_count: uint = 0;
    let mut total_word_count: uint = 0;
    let mut total_char_count: uint = 0;
    let mut total_byte_count: uint = 0;
    let mut total_longest_line_length: uint = 0;

    let mut results: ~[Result] = ~[];
    let mut max_str_len: uint = 0;

    for path in files.iter() {
        let mut reader = match open(path.to_owned()) {
            Some(f) => f,
            None => { continue }
        };

        let mut line_count: uint = 0;
        let mut word_count: uint = 0;
        let mut byte_count: uint = 0;
        let mut char_count: uint = 0;
        let mut current_char_count: uint = 0;
        let mut longest_line_length: uint = 0;

        loop {
            // reading from a TTY seems to raise a condition on, rather than return Some(0) like a file.
            // hence the option wrapped in a result here
            match result(| | reader.read_until(LF)) {
                Ok(Some(raw_line)) => {
                    // GNU 'wc' only counts lines that end in LF as lines
                    if (raw_line.iter().last().unwrap() == &LF) {
                        line_count += 1;
                    }

                    byte_count += raw_line.iter().len();

                    // try and convert the bytes to UTF-8 first
                    match from_utf8_opt(raw_line) {
                        Some(line) => {
                            word_count += line.words().len();
                            current_char_count = line.chars().len();
                            char_count += current_char_count;
                        },
                        None => {
                            word_count += raw_line.split(|&x| is_word_seperator(x)).len();
                            for byte in raw_line.iter() {
                                match byte.is_ascii() {
                                    true => {
                                        current_char_count += 1;
                                    }
                                    false => { }
                                }
                            }
                            char_count += current_char_count;
                        }
                    }

                    if (current_char_count > longest_line_length) {
                        // we subtract one here because `line.iter().len()` includes the LF
                        // matches GNU 'wc' behaviour
                        longest_line_length = current_char_count - 1;
                    }
                },
                _ => break
            }

        }

        results.push(Result {
            filename: path.clone(),
            bytes: byte_count,
            chars: char_count,
            lines: line_count,
            words: word_count,
            max_line_length: longest_line_length,
        });

        total_line_count += line_count;
        total_word_count += word_count;
        total_char_count += char_count;
        total_byte_count += byte_count;

        if (longest_line_length > total_longest_line_length) {
            total_longest_line_length = longest_line_length;
        }

        // used for formatting
        max_str_len = total_byte_count.to_str().len();
    }

    for result in results.iter() {
        print_stats(&result.filename, result.lines, result.words, result.chars, result.bytes, result.max_line_length, matches, max_str_len);
    }

    if (files.len() > 1) {
        print_stats(&~"total", total_line_count, total_word_count, total_char_count, total_byte_count, total_longest_line_length, matches, max_str_len);
    }
}

fn print_stats(filename: &~str, line_count: uint, word_count: uint, char_count: uint,
    byte_count: uint, longest_line_length: uint, matches: &Matches, max_str_len: uint) {
    if (matches.opt_present("lines")) {
        print!("{:1$}", line_count, max_str_len);
    }
    if (matches.opt_present("words")) {
        print!("{:1$}", word_count, max_str_len);
    }
    if (matches.opt_present("bytes")) {
        print!("{:1$}", byte_count, max_str_len + 1);
    }
    if (matches.opt_present("chars")) {
        print!("{:1$}", char_count, max_str_len);
    }
    if (matches.opt_present("max-line-length")) {
        print!("{:1$}", longest_line_length, max_str_len);
    }

    // defaults
    if (!matches.opt_present("bytes") && !matches.opt_present("chars") && !matches.opt_present("lines")
        && !matches.opt_present("words") && !matches.opt_present("max-line-length")) {
        print!("{:1$}", line_count, max_str_len);
        print!("{:1$}", word_count, max_str_len + 1);
        print!("{:1$}", byte_count, max_str_len + 1);
    }

    if (*filename != ~"-") {
        println!(" {}", *filename);
    }
    else {
        println!("");
    }
}

fn open(path: ~str) -> Option<BufferedReader<~Reader>> {
    if "-" == path {
        let reader = ~stdin() as ~Reader;
        return Some(BufferedReader::new(reader));
    }

    match result(|| File::open(&std::path::Path::new(path.as_slice()))) {
        Ok(fd) => {
            let reader = ~fd as ~Reader;
            return Some(BufferedReader::new(reader));
        },
        Err(e) => {
            writeln!(&mut stderr() as &mut Writer, "wc: {0:s}: {1:s}", path, e.desc.to_str());
            os::set_exit_status(1);
        }
    }

    None
}
<commit_msg>fix wc due from_utf8 changes and resolve warnings<commit_after>#[crate_id(name="wc", vers="1.0.0", author="Boden Garman")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Boden Garman <bpgarman@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::{print, stdin, stderr, File, result, BufferedReader};
use std::str::from_utf8;
use extra::getopts::{groups, Matches};

struct Result {
    filename: ~str,
    bytes: uint,
    chars: uint,
    lines: uint,
    words: uint,
    max_line_length: uint,
}

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        groups::optflag("c", "bytes", "print the byte counts"),
        groups::optflag("m", "chars", "print the character counts"),
        groups::optflag("l", "lines", "print the newline counts"),
        groups::optflag("L", "max-line-length", "print the length of the longest line"),
        groups::optflag("w", "words", "print the word counts"),
        groups::optflag("h", "help", "display this help and exit"),
        groups::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                   "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println!("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println!("");
        print(groups::usage("Print newline, word and byte counts for each FILE", opts));
        println!("");
        println!("With no FILE, or when FILE is -, read standard input.");
        return;
    }

    if matches.opt_present("version") {
        println!("wc 1.0.0");
        return;
    }

    let mut files = matches.free.clone();
    if files.is_empty() {
        files = ~[~"-"];
    }

    wc(files, &matches);
}

static CR: u8 = '\r' as u8;
static LF: u8 = '\n' as u8;
static SPACE: u8 = ' ' as u8;
static TAB: u8 = '\t' as u8;
static SYN: u8 = 0x16 as u8;
static FF: u8 = 0x0C as u8;

fn is_word_seperator(byte: u8) -> bool {
    byte == SPACE || byte == TAB || byte == CR || byte == SYN || byte == FF
}

pub fn wc(files: ~[~str], matches: &Matches) {
    let mut total_line_count: uint = 0;
    let mut total_word_count: uint = 0;
    let mut total_char_count: uint = 0;
    let mut total_byte_count: uint = 0;
    let mut total_longest_line_length: uint = 0;

    let mut results: ~[Result] = ~[];
    let mut max_str_len: uint = 0;

    for path in files.iter() {
        let mut reader = match open(path.to_owned()) {
            Some(f) => f,
            None => { continue }
        };

        let mut line_count: uint = 0;
        let mut word_count: uint = 0;
        let mut byte_count: uint = 0;
        let mut char_count: uint = 0;
        let mut current_char_count: uint = 0;
        let mut longest_line_length: uint = 0;

        loop {
            // reading from a TTY seems to raise a condition on, rather than return Some(0) like a file.
            // hence the option wrapped in a result here
            match result(| | reader.read_until(LF)) {
                Ok(Some(raw_line)) => {
                    // GNU 'wc' only counts lines that end in LF as lines
                    if raw_line.iter().last().unwrap() == &LF {
                        line_count += 1;
                    }

                    byte_count += raw_line.iter().len();

                    // try and convert the bytes to UTF-8 first
                    match from_utf8(raw_line) {
                        Some(line) => {
                            word_count += line.words().len();
                            current_char_count = line.chars().len();
                            char_count += current_char_count;
                        },
                        None => {
                            word_count += raw_line.split(|&x| is_word_seperator(x)).len();
                            for byte in raw_line.iter() {
                                match byte.is_ascii() {
                                    true => {
                                        current_char_count += 1;
                                    }
                                    false => { }
                                }
                            }
                            char_count += current_char_count;
                        }
                    }

                    if current_char_count > longest_line_length {
                        // we subtract one here because `line.iter().len()` includes the LF
                        // matches GNU 'wc' behaviour
                        longest_line_length = current_char_count - 1;
                    }
                },
                _ => break
            }

        }

        results.push(Result {
            filename: path.clone(),
            bytes: byte_count,
            chars: char_count,
            lines: line_count,
            words: word_count,
            max_line_length: longest_line_length,
        });

        total_line_count += line_count;
        total_word_count += word_count;
        total_char_count += char_count;
        total_byte_count += byte_count;

        if longest_line_length > total_longest_line_length {
            total_longest_line_length = longest_line_length;
        }

        // used for formatting
        max_str_len = total_byte_count.to_str().len();
    }

    for result in results.iter() {
        print_stats(&result.filename, result.lines, result.words, result.chars, result.bytes, result.max_line_length, matches, max_str_len);
    }

    if files.len() > 1 {
        print_stats(&~"total", total_line_count, total_word_count, total_char_count, total_byte_count, total_longest_line_length, matches, max_str_len);
    }
}

fn print_stats(filename: &~str, line_count: uint, word_count: uint, char_count: uint,
    byte_count: uint, longest_line_length: uint, matches: &Matches, max_str_len: uint) {
    if matches.opt_present("lines") {
        print!("{:1$}", line_count, max_str_len);
    }
    if matches.opt_present("words") {
        print!("{:1$}", word_count, max_str_len);
    }
    if matches.opt_present("bytes") {
        print!("{:1$}", byte_count, max_str_len + 1);
    }
    if matches.opt_present("chars") {
        print!("{:1$}", char_count, max_str_len);
    }
    if matches.opt_present("max-line-length") {
        print!("{:1$}", longest_line_length, max_str_len);
    }

    // defaults
    if !matches.opt_present("bytes")
        && !matches.opt_present("chars")
        && !matches.opt_present("lines")
        && !matches.opt_present("words")
        && !matches.opt_present("max-line-length") {
        print!("{:1$}", line_count, max_str_len);
        print!("{:1$}", word_count, max_str_len + 1);
        print!("{:1$}", byte_count, max_str_len + 1);
    }

    if *filename != ~"-" {
        println!(" {}", *filename);
    }
    else {
        println!("");
    }
}

fn open(path: ~str) -> Option<BufferedReader<~Reader>> {
    if "-" == path {
        let reader = ~stdin() as ~Reader;
        return Some(BufferedReader::new(reader));
    }

    match result(|| File::open(&std::path::Path::new(path.as_slice()))) {
        Ok(fd) => {
            let reader = ~fd as ~Reader;
            return Some(BufferedReader::new(reader));
        },
        Err(e) => {
            writeln!(&mut stderr() as &mut Writer, "wc: {0:s}: {1:s}", path, e.desc.to_str());
            os::set_exit_status(1);
        }
    }

    None
}
<|endoftext|>


<commit_before>#[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
mod c;

pub use c::*;

pub fn version() -> String {
    unsafe { format!("{}.{}.{}", rocks_version_major(), rocks_version_minor(), rocks_version_patch()) }
}

#[test]
fn test_smoke() {
    assert!(version().len() > 0);
}
<commit_msg>rocks-sys: fix static-link with bzip2<commit_after>#[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
mod c;

pub use c::*;

pub fn version() -> String {
    unsafe {
        format!(
            "{}.{}.{}",
            rocks_version_major(),
            rocks_version_minor(),
            rocks_version_patch()
        )
    }
}

#[test]
fn test_smoke() {
    assert!(version().len() > 0);
}

#[no_mangle]
pub extern "C" fn bz_internal_error(errcode: i32) {
    assert!(errcode == 0);
}
<|endoftext|>


<commit_before>//! This module handles auto-magic editing actions applied together with users
//! edits. For example, if the user typed
//!
//! ```text
//!     foo
//!         .bar()
//!         .baz()
//!     |   // <- cursor is here
//! ```
//!
//! and types `.` next, we want to indent the dot.
//!
//! Language server executes such typing assists synchronously. That is, they
//! block user's typing and should be pretty fast for this reason!

use ra_db::{FilePosition, SourceDatabase};
use ra_fmt::leading_indent;
use ra_syntax::{
    algo::find_node_at_offset,
    ast::{self, AstToken},
    AstNode, SmolStr, SourceFile,
    SyntaxKind::*,
    SyntaxToken, TextRange, TextUnit, TokenAtOffset,
};
use ra_text_edit::TextEdit;

use crate::{db::RootDatabase, source_change::SingleFileChange, SourceChange, SourceFileEdit};

pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let comment = file
        .syntax()
        .token_at_offset(position.offset)
        .left_biased()
        .and_then(ast::Comment::cast)?;

    if comment.kind().shape.is_block() {
        return None;
    }

    let prefix = comment.prefix();
    if position.offset
        < comment.syntax().text_range().start() + TextUnit::of_str(prefix) + TextUnit::from(1)
    {
        return None;
    }

    let indent = node_indent(&file, comment.syntax())?;
    let inserted = format!("\n{}{} ", indent, prefix);
    let cursor_position = position.offset + TextUnit::of_str(&inserted);
    let edit = TextEdit::insert(position.offset, inserted);

    Some(
        SourceChange::source_file_edit(
            "on enter",
            SourceFileEdit { edit, file_id: position.file_id },
        )
        .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }),
    )
}

fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
    let ws = match file.syntax().token_at_offset(token.text_range().start()) {
        TokenAtOffset::Between(l, r) => {
            assert!(r == *token);
            l
        }
        TokenAtOffset::Single(n) => {
            assert!(n == *token);
            return Some("".into());
        }
        TokenAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.text();
    let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(text[pos..].into())
}

pub(crate) const TRIGGER_CHARS: &str = ".=>";

pub(crate) fn on_char_typed(
    db: &RootDatabase,
    position: FilePosition,
    char_typed: char,
) -> Option<SourceChange> {
    assert!(TRIGGER_CHARS.contains(char_typed));
    let file = &db.parse(position.file_id).tree();
    assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
    let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?;
    Some(single_file_change.into_source_change(position.file_id))
}

fn on_char_typed_inner(
    file: &SourceFile,
    offset: TextUnit,
    char_typed: char,
) -> Option<SingleFileChange> {
    assert!(TRIGGER_CHARS.contains(char_typed));
    match char_typed {
        '.' => on_dot_typed(file, offset),
        '=' => on_eq_typed(file, offset),
        '>' => on_arrow_typed(file, offset),
        _ => unreachable!(),
    }
}

/// Returns an edit which should be applied after `=` was typed. Primarily,
/// this works when adding `let =`.
// FIXME: use a snippet completion instead of this hack here.
fn on_eq_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    assert_eq!(file.syntax().text().char_at(offset), Some('='));
    let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
    if let_stmt.has_semi() {
        return None;
    }
    if let Some(expr) = let_stmt.initializer() {
        let expr_range = expr.syntax().text_range();
        if expr_range.contains(offset) && offset != expr_range.start() {
            return None;
        }
        if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
            return None;
        }
    } else {
        return None;
    }
    let offset = let_stmt.syntax().text_range().end();
    Some(SingleFileChange {
        label: "add semicolon".to_string(),
        edit: TextEdit::insert(offset, ";".to_string()),
        cursor_position: None,
    })
}

/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
fn on_dot_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    assert_eq!(file.syntax().text().char_at(offset), Some('.'));
    let whitespace =
        file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;

    let current_indent = {
        let text = whitespace.text();
        let newline = text.rfind('\n')?;
        &text[newline + 1..]
    };
    let current_indent_len = TextUnit::of_str(current_indent);

    // Make sure dot is a part of call chain
    let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?;
    let prev_indent = leading_indent(field_expr.syntax())?;
    let target_indent = format!("    {}", prev_indent);
    let target_indent_len = TextUnit::of_str(&target_indent);
    if current_indent_len == target_indent_len {
        return None;
    }

    Some(SingleFileChange {
        label: "reindent dot".to_string(),
        edit: TextEdit::replace(
            TextRange::from_to(offset - current_indent_len, offset),
            target_indent,
        ),
        cursor_position: Some(
            offset + target_indent_len - current_indent_len + TextUnit::of_char('.'),
        ),
    })
}

/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
fn on_arrow_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    let file_text = file.syntax().text();
    assert_eq!(file_text.char_at(offset), Some('>'));
    let after_arrow = offset + TextUnit::of_char('>');
    if file_text.char_at(after_arrow) != Some('{') {
        return None;
    }
    if find_node_at_offset::<ast::RetType>(file.syntax(), offset).is_none() {
        return None;
    }

    Some(SingleFileChange {
        label: "add space after return type".to_string(),
        edit: TextEdit::insert(after_arrow, " ".to_string()),
        cursor_position: Some(after_arrow),
    })
}

#[cfg(test)]
mod tests {
    use test_utils::{add_cursor, assert_eq_text, extract_offset};

    use crate::mock_analysis::single_file;

    use super::*;

    #[test]
    fn test_on_enter() {
        fn apply_on_enter(before: &str) -> Option<String> {
            let (offset, before) = extract_offset(before);
            let (analysis, file_id) = single_file(&before);
            let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;

            assert_eq!(result.source_file_edits.len(), 1);
            let actual = result.source_file_edits[0].edit.apply(&before);
            let actual = add_cursor(&actual, result.cursor_position.unwrap().offset);
            Some(actual)
        }

        fn do_check(before: &str, after: &str) {
            let actual = apply_on_enter(before).unwrap();
            assert_eq_text!(after, &actual);
        }

        fn do_check_noop(text: &str) {
            assert!(apply_on_enter(text).is_none())
        }

        do_check(
            r"
/// Some docs<|>
fn foo() {
}
",
            r"
/// Some docs
/// <|>
fn foo() {
}
",
        );
        do_check(
            r"
impl S {
    /// Some<|> docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// <|> docs.
    fn foo() {}
}
",
        );
        do_check_noop(r"<|>//! docz");
    }

    fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> {
        let (offset, before) = extract_offset(before);
        let edit = TextEdit::insert(offset, char_typed.to_string());
        let before = edit.apply(&before);
        let parse = SourceFile::parse(&before);
        on_char_typed_inner(&parse.tree(), offset, char_typed)
            .map(|it| (it.edit.apply(&before), it))
    }

    fn type_char(char_typed: char, before: &str, after: &str) {
        let (actual, file_change) = do_type_char(char_typed, before)
            .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));

        if after.contains("<|>") {
            let (offset, after) = extract_offset(after);
            assert_eq_text!(&after, &actual);
            assert_eq!(file_change.cursor_position, Some(offset))
        } else {
            assert_eq_text!(after, &actual);
        }
    }

    fn type_char_noop(char_typed: char, before: &str) {
        let file_change = do_type_char(char_typed, before);
        assert!(file_change.is_none())
    }

    #[test]
    fn test_on_eq_typed() {
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        // }
        // ");
        type_char(
            '=',
            r"
fn foo() {
    let foo <|> 1 + 1
}
",
            r"
fn foo() {
    let foo = 1 + 1;
}
",
        );
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        //     let bar = 1;
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        //     let bar = 1;
        // }
        // ");
    }

    #[test]
    fn indents_new_chain_call() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>
            }
            ",
        )
    }

    #[test]
    fn indents_new_chain_call_with_semi() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>;
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .;
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>;
            }
            ",
        )
    }

    #[test]
    fn indents_continued_chain_call() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    <|>
            }
            ",
        );
    }

    #[test]
    fn indents_middle_of_chain_call() {
        type_char(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                <|>
                    .nth(92)
                    .unwrap();
            }
            ",
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    .
                    .nth(92)
                    .unwrap();
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    <|>
                    .nth(92)
                    .unwrap();
            }
            ",
        );
    }

    #[test]
    fn dont_indent_freestanding_dot() {
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                <|>
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
            <|>
            }
            ",
        );
    }

    #[test]
    fn adds_space_after_return_type() {
        type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }")
    }
}
<commit_msg>Don't continue line comments<commit_after>//! This module handles auto-magic editing actions applied together with users
//! edits. For example, if the user typed
//!
//! ```text
//!     foo
//!         .bar()
//!         .baz()
//!     |   // <- cursor is here
//! ```
//!
//! and types `.` next, we want to indent the dot.
//!
//! Language server executes such typing assists synchronously. That is, they
//! block user's typing and should be pretty fast for this reason!

use ra_db::{FilePosition, SourceDatabase};
use ra_fmt::leading_indent;
use ra_syntax::{
    algo::find_node_at_offset,
    ast::{self, AstToken},
    AstNode, SmolStr, SourceFile,
    SyntaxKind::*,
    SyntaxToken, TextRange, TextUnit, TokenAtOffset,
};
use ra_text_edit::TextEdit;

use crate::{db::RootDatabase, source_change::SingleFileChange, SourceChange, SourceFileEdit};

pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let comment = file
        .syntax()
        .token_at_offset(position.offset)
        .left_biased()
        .and_then(ast::Comment::cast)?;

    if comment.kind().shape.is_block() {
        return None;
    }

    let prefix = comment.prefix();
    let comment_range = comment.syntax().text_range();
    if position.offset < comment_range.start() + TextUnit::of_str(prefix) + TextUnit::from(1) {
        return None;
    }

    // Continuing non-doc line comments (like this one :) ) is annoying
    if prefix == "//" && comment_range.end() == position.offset {
        return None;
    }

    let indent = node_indent(&file, comment.syntax())?;
    let inserted = format!("\n{}{} ", indent, prefix);
    let cursor_position = position.offset + TextUnit::of_str(&inserted);
    let edit = TextEdit::insert(position.offset, inserted);

    Some(
        SourceChange::source_file_edit(
            "on enter",
            SourceFileEdit { edit, file_id: position.file_id },
        )
        .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }),
    )
}

fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
    let ws = match file.syntax().token_at_offset(token.text_range().start()) {
        TokenAtOffset::Between(l, r) => {
            assert!(r == *token);
            l
        }
        TokenAtOffset::Single(n) => {
            assert!(n == *token);
            return Some("".into());
        }
        TokenAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.text();
    let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(text[pos..].into())
}

pub(crate) const TRIGGER_CHARS: &str = ".=>";

pub(crate) fn on_char_typed(
    db: &RootDatabase,
    position: FilePosition,
    char_typed: char,
) -> Option<SourceChange> {
    assert!(TRIGGER_CHARS.contains(char_typed));
    let file = &db.parse(position.file_id).tree();
    assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
    let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?;
    Some(single_file_change.into_source_change(position.file_id))
}

fn on_char_typed_inner(
    file: &SourceFile,
    offset: TextUnit,
    char_typed: char,
) -> Option<SingleFileChange> {
    assert!(TRIGGER_CHARS.contains(char_typed));
    match char_typed {
        '.' => on_dot_typed(file, offset),
        '=' => on_eq_typed(file, offset),
        '>' => on_arrow_typed(file, offset),
        _ => unreachable!(),
    }
}

/// Returns an edit which should be applied after `=` was typed. Primarily,
/// this works when adding `let =`.
// FIXME: use a snippet completion instead of this hack here.
fn on_eq_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    assert_eq!(file.syntax().text().char_at(offset), Some('='));
    let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
    if let_stmt.has_semi() {
        return None;
    }
    if let Some(expr) = let_stmt.initializer() {
        let expr_range = expr.syntax().text_range();
        if expr_range.contains(offset) && offset != expr_range.start() {
            return None;
        }
        if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
            return None;
        }
    } else {
        return None;
    }
    let offset = let_stmt.syntax().text_range().end();
    Some(SingleFileChange {
        label: "add semicolon".to_string(),
        edit: TextEdit::insert(offset, ";".to_string()),
        cursor_position: None,
    })
}

/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
fn on_dot_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    assert_eq!(file.syntax().text().char_at(offset), Some('.'));
    let whitespace =
        file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;

    let current_indent = {
        let text = whitespace.text();
        let newline = text.rfind('\n')?;
        &text[newline + 1..]
    };
    let current_indent_len = TextUnit::of_str(current_indent);

    // Make sure dot is a part of call chain
    let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?;
    let prev_indent = leading_indent(field_expr.syntax())?;
    let target_indent = format!("    {}", prev_indent);
    let target_indent_len = TextUnit::of_str(&target_indent);
    if current_indent_len == target_indent_len {
        return None;
    }

    Some(SingleFileChange {
        label: "reindent dot".to_string(),
        edit: TextEdit::replace(
            TextRange::from_to(offset - current_indent_len, offset),
            target_indent,
        ),
        cursor_position: Some(
            offset + target_indent_len - current_indent_len + TextUnit::of_char('.'),
        ),
    })
}

/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
fn on_arrow_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    let file_text = file.syntax().text();
    assert_eq!(file_text.char_at(offset), Some('>'));
    let after_arrow = offset + TextUnit::of_char('>');
    if file_text.char_at(after_arrow) != Some('{') {
        return None;
    }
    if find_node_at_offset::<ast::RetType>(file.syntax(), offset).is_none() {
        return None;
    }

    Some(SingleFileChange {
        label: "add space after return type".to_string(),
        edit: TextEdit::insert(after_arrow, " ".to_string()),
        cursor_position: Some(after_arrow),
    })
}

#[cfg(test)]
mod tests {
    use test_utils::{add_cursor, assert_eq_text, extract_offset};

    use crate::mock_analysis::single_file;

    use super::*;

    #[test]
    fn test_on_enter() {
        fn apply_on_enter(before: &str) -> Option<String> {
            let (offset, before) = extract_offset(before);
            let (analysis, file_id) = single_file(&before);
            let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;

            assert_eq!(result.source_file_edits.len(), 1);
            let actual = result.source_file_edits[0].edit.apply(&before);
            let actual = add_cursor(&actual, result.cursor_position.unwrap().offset);
            Some(actual)
        }

        fn do_check(before: &str, after: &str) {
            let actual = apply_on_enter(before).unwrap();
            assert_eq_text!(after, &actual);
        }

        fn do_check_noop(text: &str) {
            assert!(apply_on_enter(text).is_none())
        }

        do_check(
            r"
/// Some docs<|>
fn foo() {
}
",
            r"
/// Some docs
/// <|>
fn foo() {
}
",
        );
        do_check(
            r"
impl S {
    /// Some<|> docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// <|> docs.
    fn foo() {}
}
",
        );
        do_check(
            r"
fn main() {
    // Fix<|> me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // <|> me
    let x = 1 + 1;
}
",
        );
        do_check_noop(
            r"
fn main() {
    // Fix me<|>
    let x = 1 + 1;
}
",
        );

        do_check_noop(r"<|>//! docz");
    }

    fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> {
        let (offset, before) = extract_offset(before);
        let edit = TextEdit::insert(offset, char_typed.to_string());
        let before = edit.apply(&before);
        let parse = SourceFile::parse(&before);
        on_char_typed_inner(&parse.tree(), offset, char_typed)
            .map(|it| (it.edit.apply(&before), it))
    }

    fn type_char(char_typed: char, before: &str, after: &str) {
        let (actual, file_change) = do_type_char(char_typed, before)
            .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));

        if after.contains("<|>") {
            let (offset, after) = extract_offset(after);
            assert_eq_text!(&after, &actual);
            assert_eq!(file_change.cursor_position, Some(offset))
        } else {
            assert_eq_text!(after, &actual);
        }
    }

    fn type_char_noop(char_typed: char, before: &str) {
        let file_change = do_type_char(char_typed, before);
        assert!(file_change.is_none())
    }

    #[test]
    fn test_on_eq_typed() {
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        // }
        // ");
        type_char(
            '=',
            r"
fn foo() {
    let foo <|> 1 + 1
}
",
            r"
fn foo() {
    let foo = 1 + 1;
}
",
        );
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        //     let bar = 1;
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        //     let bar = 1;
        // }
        // ");
    }

    #[test]
    fn indents_new_chain_call() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>
            }
            ",
        )
    }

    #[test]
    fn indents_new_chain_call_with_semi() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>;
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .;
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>;
            }
            ",
        )
    }

    #[test]
    fn indents_continued_chain_call() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    <|>
            }
            ",
        );
    }

    #[test]
    fn indents_middle_of_chain_call() {
        type_char(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                <|>
                    .nth(92)
                    .unwrap();
            }
            ",
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    .
                    .nth(92)
                    .unwrap();
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    <|>
                    .nth(92)
                    .unwrap();
            }
            ",
        );
    }

    #[test]
    fn dont_indent_freestanding_dot() {
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                <|>
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
            <|>
            }
            ",
        );
    }

    #[test]
    fn adds_space_after_return_type() {
        type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }")
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// DOCS
// generic?
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Base64<B = Vec<u8>> {
    bytes: B,
}

const BASE64_CONFIG: base64::Config = base64::STANDARD_NO_PAD.decode_allow_trailing_bits(true);

impl<B: AsRef<[u8]>> Base64<B> {
    /// Create a `Base64` instance from raw bytes, to be base64-encoded in serialialization.
    pub fn new(bytes: B) -> Self {
        Self { bytes }
    }

    /// Get the raw bytes held by this `Base64` instance.
    pub fn as_bytes(&self) -> &[u8] {
        self.bytes.as_ref()
    }

    /// Encode the bytes contained in this `Base64` instance to unpadded base64.
    pub fn encode(&self) -> String {
        base64::encode_config(&self.bytes, BASE64_CONFIG)
    }
}

impl Base64 {
    /// Create a `Base64` instance containing an empty `Vec<u8>`.
    pub fn empty() -> Self {
        Self { bytes: Vec::new() }
    }

    /// Parse some base64-encoded data to create a `Base64` instance.
    pub fn parse(encoded: impl AsRef<[u8]>) -> Result<Self, base64::DecodeError> {
        base64::decode_config(encoded, BASE64_CONFIG).map(Self::new)
    }
}

impl<B: AsRef<[u8]>> fmt::Debug for Base64<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<B: AsRef<[u8]>> fmt::Display for Base64<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<'de> Deserialize<'de> for Base64 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let encoded = crate::deserialize_cow_str(deserializer)?;
        Self::parse(&*encoded).map_err(de::Error::custom)
    }
}

impl<B: AsRef<[u8]>> Serialize for Base64<B> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.encode())
    }
}
<commit_msg>serde: Update base64.rs with a comment<commit_after>use std::fmt;

use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// DOCS
// generic?
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Base64<B = Vec<u8>> {
    bytes: B,
}

// See https://github.com/matrix-org/matrix-doc/issues/3211
const BASE64_CONFIG: base64::Config = base64::STANDARD_NO_PAD.decode_allow_trailing_bits(true);

impl<B: AsRef<[u8]>> Base64<B> {
    /// Create a `Base64` instance from raw bytes, to be base64-encoded in serialialization.
    pub fn new(bytes: B) -> Self {
        Self { bytes }
    }

    /// Get the raw bytes held by this `Base64` instance.
    pub fn as_bytes(&self) -> &[u8] {
        self.bytes.as_ref()
    }

    /// Encode the bytes contained in this `Base64` instance to unpadded base64.
    pub fn encode(&self) -> String {
        base64::encode_config(&self.bytes, BASE64_CONFIG)
    }
}

impl Base64 {
    /// Create a `Base64` instance containing an empty `Vec<u8>`.
    pub fn empty() -> Self {
        Self { bytes: Vec::new() }
    }

    /// Parse some base64-encoded data to create a `Base64` instance.
    pub fn parse(encoded: impl AsRef<[u8]>) -> Result<Self, base64::DecodeError> {
        base64::decode_config(encoded, BASE64_CONFIG).map(Self::new)
    }
}

impl<B: AsRef<[u8]>> fmt::Debug for Base64<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<B: AsRef<[u8]>> fmt::Display for Base64<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<'de> Deserialize<'de> for Base64 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let encoded = crate::deserialize_cow_str(deserializer)?;
        Self::parse(&*encoded).map_err(de::Error::custom)
    }
}

impl<B: AsRef<[u8]>> Serialize for Base64<B> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.encode())
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;

use ra_syntax::{
    ast::{self, AstNode},
    File, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange,
};

use crate::db::SyntaxDatabase;
use crate::FileId;

salsa::query_group! {
    pub(crate) trait SyntaxPtrDatabase: SyntaxDatabase {
        fn resolve_syntax_ptr(ptr: SyntaxPtr) -> SyntaxNode {
            type ResolveSyntaxPtrQuery;
            // Don't retain syntax trees in memory
            storage volatile;
        }
    }
}

fn resolve_syntax_ptr(db: &impl SyntaxDatabase, ptr: SyntaxPtr) -> SyntaxNode {
    let syntax = db.file_syntax(ptr.file_id);
    ptr.local.resolve(&syntax)
}

/// SyntaxPtr is a cheap `Copy` id which identifies a particular syntax node,
/// without retaining syntax tree in memory. You need to explicitly `resolve`
/// `SyntaxPtr` to get a `SyntaxNode`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct SyntaxPtr {
    file_id: FileId,
    local: LocalSyntaxPtr,
}

impl SyntaxPtr {
    pub(crate) fn new(file_id: FileId, node: SyntaxNodeRef) -> SyntaxPtr {
        let local = LocalSyntaxPtr::new(node);
        SyntaxPtr { file_id, local }
    }
}

struct OwnedAst<T> {
    syntax: SyntaxNode,
    phantom: PhantomData<T>,
}

trait ToAst {
    type Ast;
    fn to_ast(self) -> Self::Ast;
}

impl<'a> ToAst for &'a OwnedAst<ast::FnDef<'static>> {
    type Ast = ast::FnDef<'a>;
    fn to_ast(self) -> ast::FnDef<'a> {
        ast::FnDef::cast(self.syntax.borrowed()).unwrap()
    }
}

/// A pionter to a syntax node inside a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct LocalSyntaxPtr {
    range: TextRange,
    kind: SyntaxKind,
}

impl LocalSyntaxPtr {
    pub(crate) fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr {
        LocalSyntaxPtr {
            range: node.range(),
            kind: node.kind(),
        }
    }

    pub(crate) fn resolve(self, file: &File) -> SyntaxNode {
        let mut curr = file.syntax();
        loop {
            if curr.range() == self.range && curr.kind() == self.kind {
                return curr.owned();
            }
            curr = curr
                .children()
                .find(|it| self.range.is_subrange(&it.range()))
                .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self))
        }
    }

    pub(crate) fn into_global(self, file_id: FileId) -> SyntaxPtr {
        SyntaxPtr {
            file_id,
            local: self,
        }
    }
}

#[test]
fn test_local_syntax_ptr() {
    let file = File::parse("struct Foo { f: u32, }");
    let field = file
        .syntax()
        .descendants()
        .find_map(ast::NamedFieldDef::cast)
        .unwrap();
    let ptr = LocalSyntaxPtr::new(field.syntax());
    let field_syntax = ptr.resolve(&file);
    assert_eq!(field.syntax(), field_syntax);
}
<commit_msg>RemoveDeadCode<commit_after>use std::marker::PhantomData;

use ra_syntax::{
    ast::{self, AstNode},
    File, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange,
};

use crate::db::SyntaxDatabase;
use crate::FileId;

salsa::query_group! {
    pub(crate) trait SyntaxPtrDatabase: SyntaxDatabase {
        fn resolve_syntax_ptr(ptr: SyntaxPtr) -> SyntaxNode {
            type ResolveSyntaxPtrQuery;
            // Don't retain syntax trees in memory
            storage volatile;
        }
    }
}

fn resolve_syntax_ptr(db: &impl SyntaxDatabase, ptr: SyntaxPtr) -> SyntaxNode {
    let syntax = db.file_syntax(ptr.file_id);
    ptr.local.resolve(&syntax)
}

/// SyntaxPtr is a cheap `Copy` id which identifies a particular syntax node,
/// without retaining syntax tree in memory. You need to explicitly `resolve`
/// `SyntaxPtr` to get a `SyntaxNode`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct SyntaxPtr {
    file_id: FileId,
    local: LocalSyntaxPtr,
}

impl SyntaxPtr {
    pub(crate) fn new(file_id: FileId, node: SyntaxNodeRef) -> SyntaxPtr {
        let local = LocalSyntaxPtr::new(node);
        SyntaxPtr { file_id, local }
    }
}

/// A pionter to a syntax node inside a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct LocalSyntaxPtr {
    range: TextRange,
    kind: SyntaxKind,
}

impl LocalSyntaxPtr {
    pub(crate) fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr {
        LocalSyntaxPtr {
            range: node.range(),
            kind: node.kind(),
        }
    }

    pub(crate) fn resolve(self, file: &File) -> SyntaxNode {
        let mut curr = file.syntax();
        loop {
            if curr.range() == self.range && curr.kind() == self.kind {
                return curr.owned();
            }
            curr = curr
                .children()
                .find(|it| self.range.is_subrange(&it.range()))
                .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self))
        }
    }

    pub(crate) fn into_global(self, file_id: FileId) -> SyntaxPtr {
        SyntaxPtr {
            file_id,
            local: self,
        }
    }
}

#[test]
fn test_local_syntax_ptr() {
    let file = File::parse("struct Foo { f: u32, }");
    let field = file
        .syntax()
        .descendants()
        .find_map(ast::NamedFieldDef::cast)
        .unwrap();
    let ptr = LocalSyntaxPtr::new(field.syntax());
    let field_syntax = ptr.resolve(&file);
    assert_eq!(field.syntax(), field_syntax);
}
<|endoftext|>