<commit_before>
use std::borrow::{ Cow };
use std::collections::{ HashMap };
use std::fmt::{ self, Write };

use maud::{ self, Escaper };
use pulldown_cmark::{ Event, Tag };

use escape::HrefEscaper;
use markdown::{ MarkdownString, MarkdownEvents };

impl<'a> maud::Render for MarkdownString<'a> {
  fn render(&self, w: &mut Write) -> fmt::Result {
    render_events(self.events(), w)
  }
}

impl<'a, I: 'a + Iterator<Item=Event<'a>>, F: Fn() -> I> maud::Render for MarkdownEvents<'a, I, F> {
  fn render(&self, w: &mut Write) -> fmt::Result {
    render_events(self.events(), w)
  }
}

fn render_events<'a, I: Iterator<Item=Event<'a>>>(events: I, mut w: &mut Write) -> fmt::Result {
  let mut numbers = HashMap::new();
  for event in events {
    try!(render_event(event, &mut numbers, &mut w));
  }
  Ok(())
}

fn render_event<'a>(event: Event<'a>, numbers: &mut HashMap<Cow<'a, str>, usize>, w: &mut Write) -> fmt::Result {
  match event {
    Event::Start(tag) => render_start_tag(tag, numbers, w),
    Event::End(tag) => render_end_tag(tag, w),
    Event::Text(text) => Escaper::new(w).write_str(&*text),
    Event::Html(html) | Event::InlineHtml(html) => w.write_str(&*html),
    Event::SoftBreak => w.write_char('\n'),
    Event::HardBreak => w.write_str("
\n"),
    Event::FootnoteReference(name) => render_footnote_reference(name, numbers, w),
  }
}

fn render_start_tag<'a>(tag: Tag<'a>, numbers: &mut HashMap<Cow<'a, str>, usize>, mut w: &mut Write) -> fmt::Result {
  match tag {
    Tag::Rule => w.write_str("
\n"),
    Tag::Code => w.write_str(""),
    Tag::Strong => w.write_str(""),
    Tag::Emphasis => w.write_str(""),
    Tag::Paragraph => w.write_str("
"),
    Tag::BlockQuote => w.write_str("
\n"),

    Tag::Table(_) => w.write_str("
"),
    Tag::TableHead => w.write_str("

"),
    Tag::TableRow => w.write_str("
"),
    Tag::TableCell => w.write_str(""),

    Tag::Item => w.write_str("
"),
    Tag::List(None) => w.write_str("
\n"),
    Tag::List(Some(1)) => w.write_str("
\n"),
    Tag::List(Some(start)) => write!(w, "
\n", start),

    Tag::Header(level) => render_header_start_tag(level as u8, w),
    Tag::CodeBlock(info) => render_code_block_start_tag(&*info, w),
    Tag::Image(src, title) => render_image_start_tag(&*src, &*title, w),
    Tag::Link(dest, title) => render_link_start_tag(&*dest, &*title, w),
    Tag::FootnoteDefinition(name) => render_footnote_definition_start_tag(name, numbers, w),
  }
}

fn render_end_tag<'a>(tag: Tag<'a>, mut w: &mut Write) -> fmt::Result {
  match tag {
    Tag::Rule => Ok(()),
    Tag::Code => w.write_str(""),
    Tag::Strong => w.write_str(""),
    Tag::Emphasis => w.write_str(""),
    Tag::Paragraph => w.write_str("
\n"),
    Tag::BlockQuote => w.write_str("
\n"),

    Tag::Table(_) => w.write_str("
\n"),
    Tag::TableHead => w.write_str("

\n"),
    Tag::TableRow => w.write_str("
\n"),
    Tag::TableCell => w.write_str("	"),

    Tag::Item => w.write_str("
\n"),
    Tag::List(None) => w.write_str("
\n"),
    Tag::List(Some(_)) => w.write_str("
\n"),

    Tag::Header(level) => render_header_end_tag(level as u8, w),
    Tag::CodeBlock(_) => w.write_str("
\n"),
    Tag::Image(_, _) => Ok(()),
    Tag::Link(_, _) => w.write_str(""),
    Tag::FootnoteDefinition(_) => w.write_str("
\n"),
  }
}

fn render_link_start_tag(dest: &str, title: &str, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("")
}

fn render_image_start_tag(src: &str, title: &str, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("")
}

fn render_header_start_tag(level: u8, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("<h"));
  try!(w.write_char((b'0' + level) as char));
  w.write_char('>')
}

fn render_header_end_tag(level: u8, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("</h"));
  try!(w.write_char((b'0' + level) as char));
  w.write_str(">\n")
}

fn render_code_block_start_tag(info: &str, mut w: &mut Write) -> fmt::Result {
  let lang = info.split(' ').next().unwrap();
  if lang.is_empty() {
    w.write_str("
")
  } else {
    try!(w.write_str("
")
  }
}

fn render_footnote_definition_start_tag<'a>(name: Cow<'a, str>, numbers: &mut HashMap<Cow<'a, str>, usize>, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("
{}", number)
}

fn render_footnote_reference<'a>(name: Cow<'a, str>, numbers: &mut HashMap<Cow<'a, str>, usize>, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("{}", number)
}

<commit_msg>Fix image alt text<commit_after>
use std::borrow::{ Cow };
use std::collections::{ HashMap };
use std::fmt::{ self, Write };

use maud::{ self, Escaper };
use pulldown_cmark::{ Event, Tag };

use escape::HrefEscaper;
use markdown::{ MarkdownString, MarkdownEvents };

struct Context<'a> {
  numbers: HashMap<Cow<'a, str>, usize>,
  within_image: bool,
}

impl<'a> maud::Render for MarkdownString<'a> {
  fn render(&self, w: &mut Write) -> fmt::Result {
    render_events(self.events(), w)
  }
}

impl<'a, I: 'a + Iterator<Item=Event<'a>>, F: Fn() -> I> maud::Render for MarkdownEvents<'a, I, F> {
  fn render(&self, w: &mut Write) -> fmt::Result {
    render_events(self.events(), w)
  }
}

fn render_events<'a, I: Iterator<Item=Event<'a>>>(events: I, mut w: &mut Write) -> fmt::Result {
  let mut context = Context {
    numbers: HashMap::new(),
    within_image: false,
  };
  for event in events {
    if context.within_image {
      try!(render_event_within_image(event, &mut context, &mut w));
    } else {
      try!(render_event(event, &mut context, &mut w));
    }
  }
  Ok(())
}

fn render_event<'a>(event: Event<'a>, context: &mut Context<'a>, w: &mut Write) -> fmt::Result {
  match event {
    Event::Start(tag) => render_start_tag(tag, context, w),
    Event::End(tag) => render_end_tag(tag, w),
    Event::Text(text) => Escaper::new(w).write_str(&*text),
    Event::Html(html) | Event::InlineHtml(html) => w.write_str(&*html),
    Event::SoftBreak => w.write_char('\n'),
    Event::HardBreak => w.write_str("
\n"),
    Event::FootnoteReference(name) => render_footnote_reference(name, &mut context.numbers, w),
  }
}

fn render_event_within_image<'a>(event: Event<'a>, context: &mut Context<'a>, w: &mut Write) -> fmt::Result {
  match event {
    Event::Start(_) => Err(fmt::Error),
    Event::End(Tag::Image(_, title)) => {
      context.within_image = false;
      render_image_end_tag(&*title, w)
    },
    Event::End(_) => Err(fmt::Error),
    Event::Text(text) => Escaper::new(w).write_str(&*text),
    Event::Html(_) => Err(fmt::Error),
    Event::InlineHtml(html) => Escaper::new(w).write_str(&*html),
    Event::SoftBreak | Event::HardBreak => w.write_char(' '),
    Event::FootnoteReference(name) => render_footnote_reference_within_image(name, &mut context.numbers, w),
  }
}

fn render_start_tag<'a>(tag: Tag<'a>, context: &mut Context<'a>, mut w: &mut Write) -> fmt::Result {
  match tag {
    Tag::Rule => w.write_str("
\n"),
    Tag::Code => w.write_str(""),
    Tag::Strong => w.write_str(""),
    Tag::Emphasis => w.write_str(""),
    Tag::Paragraph => w.write_str("
"),
    Tag::BlockQuote => w.write_str("
\n"),

    Tag::Table(_) => w.write_str("
"),
    Tag::TableHead => w.write_str("

"),
    Tag::TableRow => w.write_str("
"),
    Tag::TableCell => w.write_str(""),

    Tag::Item => w.write_str("
"),
    Tag::List(None) => w.write_str("
\n"),
    Tag::List(Some(1)) => w.write_str("
\n"),
    Tag::List(Some(start)) => write!(w, "
\n", start),

    Tag::Header(level) => render_header_start_tag(level as u8, w),
    Tag::CodeBlock(info) => render_code_block_start_tag(&*info, w),
    Tag::Image(src, _) => {
      context.within_image = false;
      render_image_start_tag(&*src, w)
    },
    Tag::Link(dest, title) => render_link_start_tag(&*dest, &*title, w),
    Tag::FootnoteDefinition(name) => render_footnote_definition_start_tag(name, &mut context.numbers, w),
  }
}

fn render_end_tag<'a>(tag: Tag<'a>, mut w: &mut Write) -> fmt::Result {
  match tag {
    Tag::Rule => Ok(()),
    Tag::Code => w.write_str(""),
    Tag::Strong => w.write_str(""),
    Tag::Emphasis => w.write_str(""),
    Tag::Paragraph => w.write_str("
\n"),
    Tag::BlockQuote => w.write_str("
\n"),

    Tag::Table(_) => w.write_str("
\n"),
    Tag::TableHead => w.write_str("

\n"),
    Tag::TableRow => w.write_str("
\n"),
    Tag::TableCell => w.write_str("	"),

    Tag::Item => w.write_str("
\n"),
    Tag::List(None) => w.write_str("
\n"),
    Tag::List(Some(_)) => w.write_str("
\n"),

    Tag::Header(level) => render_header_end_tag(level as u8, w),
    Tag::CodeBlock(_) => w.write_str("
\n"),
    Tag::Image(_, _) => Err(fmt::Error),
    Tag::Link(_, _) => w.write_str(""),
    Tag::FootnoteDefinition(_) => w.write_str("
\n"),
  }
}

fn render_link_start_tag(dest: &str, title: &str, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("")
}

fn render_image_start_tag(src: &str, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str(" fmt::Result {
  if !title.is_empty() {
    try!(w.write_str("\" title=\""));
    try!(Escaper::new(&mut w).write_str(title));
  }
  w.write_str("\" />")
}

fn render_header_start_tag(level: u8, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("<h"));
  try!(w.write_char((b'0' + level) as char));
  w.write_char('>')
}

fn render_header_end_tag(level: u8, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("</h"));
  try!(w.write_char((b'0' + level) as char));
  w.write_str(">\n")
}

fn render_code_block_start_tag(info: &str, mut w: &mut Write) -> fmt::Result {
  let lang = info.split(' ').next().unwrap();
  if lang.is_empty() {
    w.write_str("
")
  } else {
    try!(w.write_str("
")
  }
}

fn render_footnote_definition_start_tag<'a>(name: Cow<'a, str>, numbers: &mut HashMap<Cow<'a, str>, usize>, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("
{}", number)
}

fn render_footnote_reference<'a>(name: Cow<'a, str>, numbers: &mut HashMap<Cow<'a, str>, usize>, mut w: &mut Write) -> fmt::Result {
  try!(w.write_str("{}", number)
}

fn render_footnote_reference_within_image<'a>(name: Cow<'a, str>, numbers: &mut HashMap<Cow<'a, str>, usize>, mut w: &mut Write) -> fmt::Result {
  let len = numbers.len();
  let number = numbers.entry(name).or_insert(len);
  write!(w, "[{}]", number)
}

<|endoftext|>


<commit_before>//! A lexer for the CoffeeScript programming language.

use token::{Category, Token};
use tokenizer::{Tokenizer, StateFunction};

fn initial_state(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    if tokenizer.starts_with_lexeme("class") {
        tokenizer.tokenize_next(5, Category::Keyword);
        tokenizer.consume_whitespace();
        return Some(StateFunction(identifier))
    } else if tokenizer.starts_with_lexeme("if") {
        tokenizer.tokenize_next(2, Category::Keyword);
        return Some(StateFunction(whitespace))
    } else if tokenizer.starts_with_lexeme("else") {
        tokenizer.tokenize_next(4, Category::Keyword);
        return Some(StateFunction(whitespace))
    } else if tokenizer.starts_with_lexeme("return") {
        tokenizer.tokenize_next(6, Category::Keyword);
        return Some(StateFunction(whitespace))
    } else if tokenizer.starts_with_lexeme("true") {
        tokenizer.tokenize_next(4, Category::Boolean);
        return Some(StateFunction(initial_state))
    } else if tokenizer.starts_with_lexeme("false") {
        tokenizer.tokenize_next(5, Category::Boolean);
        return Some(StateFunction(initial_state))
    } else if tokenizer.starts_with_lexeme("var") {
        tokenizer.tokenize_next(3, Category::Keyword);
        tokenizer.consume_whitespace();
        return Some(StateFunction(identifier))
    } else if tokenizer.has_prefix("###") {
        tokenizer.tokenize(Category::Text);
        tokenizer.advance();
        tokenizer.advance();
        tokenizer.advance();
        return Some(StateFunction(multi_line_comment))
    }

    match tokenizer.current_char() {
        Some('"') => {
            tokenizer.tokenize(Category::Text);
            tokenizer.advance();
            Some(StateFunction(inside_string))
        },
        Some('\'') => {
            tokenizer.tokenize(Category::Text);
            tokenizer.advance();
            Some(StateFunction(inside_single_quote_string))
        },
        Some('.') => {
            tokenizer.tokenize_next(1, Category::Text);
            Some(StateFunction(initial_state))
        },
        Some('+') => {
            tokenizer.tokenize_next(1, Category::Operator);
            Some(StateFunction(initial_state))
        },
        Some(' ') | Some('\n') => {
            match tokenizer.next_non_whitespace_char() {
                Some('=') => {
                    tokenizer.tokenize(Category::Identifier);
                    tokenizer.consume_whitespace();
                    tokenizer.tokenize_next(1, Category::Text);
                },
                _ => tokenizer.consume_whitespace(),
            }
            Some(StateFunction(initial_state))
        },
        Some('=') => {
            tokenizer.tokenize(Category::Identifier);
            tokenizer.tokenize_next(1, Category::Text);
            Some(StateFunction(initial_state))
        },
        Some('#') => {
            tokenizer.tokenize(Category::Text);
            tokenizer.advance();
            Some(StateFunction(comment))
        },
        Some('$') => {
            tokenizer.tokenize_next(1, Category::Keyword);
            Some(StateFunction(initial_state))
        },
        Some('@') => {
            tokenizer.advance();
            Some(StateFunction(identifier))
        },
        Some(':') => {
            if tokenizer.starts_with_lexeme(":") {
                tokenizer.advance();
                tokenizer.tokenize(Category::Literal);
                tokenizer.consume_whitespace();
                Some(StateFunction(initial_state))
            } else {
                tokenizer.advance();
                Some(StateFunction(symbol))
            }
        },
        Some(c) => {
            tokenizer.advance();

            if c.is_numeric() {
                Some(StateFunction(integer))
            } else {
                Some(StateFunction(initial_state))
            }
        }

        None => {
            tokenizer.tokenize(Category::Text);
            None
        }
    }
}

fn inside_string(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                '"' => {
                    tokenizer.advance();
                    tokenizer.tokenize(Category::String);
                    Some(StateFunction(initial_state))
                },
                '\\' => {
                    tokenizer.advance();
                    tokenizer.advance();
                    Some(StateFunction(inside_string))
                }
                _ => {
                    tokenizer.advance();
                    Some(StateFunction(inside_string))
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::String);
            None
        }
    }
}

fn inside_single_quote_string(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                '\'' => {
                    tokenizer.advance();
                    tokenizer.tokenize(Category::String);
                    Some(StateFunction(initial_state))
                },
                '\\' => {
                    tokenizer.advance();
                    tokenizer.advance();
                    Some(StateFunction(inside_single_quote_string))
                }
                _ => {
                    tokenizer.advance();
                    Some(StateFunction(inside_single_quote_string))
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::String);
            None
        }
    }
}

fn whitespace(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                ' ' | '\n' => {
                    tokenizer.advance();
                    Some(StateFunction(whitespace))
                },
                _ => {
                    tokenizer.tokenize(Category::Whitespace);
                    match tokenizer.states.pop() {
                        Some(state) => Some(state),
                        None => Some(StateFunction(initial_state)),
                    }
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::Whitespace);
            None
        }
    }
}

fn identifier(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                ' ' | '\n' => {
                    tokenizer.tokenize(Category::Identifier);
                    Some(StateFunction(initial_state))
                },
                '|' | ')' | '-' => {
                    tokenizer.tokenize(Category::Identifier);
                    tokenizer.tokenize_next(1, Category::Text);
                    Some(StateFunction(initial_state))
                },
                _ => {
                    tokenizer.advance();
                    Some(StateFunction(identifier))
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::Identifier);
            None
        }
    }
}

fn comment(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                '\n' => {
                    tokenizer.tokenize(Category::Comment);
                    Some(StateFunction(initial_state))
                },
                _ => {
                    tokenizer.advance();
                    Some(StateFunction(comment))
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::Whitespace);
            None
        }
    }
}

fn multi_line_comment(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    if tokenizer.has_prefix("###") {
        tokenizer.advance();
        tokenizer.advance();
        tokenizer.advance();
        tokenizer.tokenize(Category::Comment);
        return Some(StateFunction(initial_state))
    }

    match tokenizer.current_char() {
        Some(_) => {
            tokenizer.advance();
            Some(StateFunction(multi_line_comment))
        },
        None => {
            tokenizer.tokenize(Category::Comment);
            None
        }
    }
}

fn integer(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            if c.is_numeric() {
                tokenizer.advance();
                Some(StateFunction(integer))
            } else {
                tokenizer.tokenize(Category::Integer);
                Some(StateFunction(initial_state))
            }
        }

        None => {
            tokenizer.tokenize(Category::Integer);
            None
        }
    }
}

fn symbol(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            if c.is_alphanumeric() || c == '_' || c == '?' {
                tokenizer.advance();
                Some(StateFunction(symbol))
            } else {
                tokenizer.tokenize(Category::Literal);
                Some(StateFunction(initial_state))
            }
        }

        None => {
            tokenizer.tokenize(Category::Literal);
            None
        }
    }
}

/// Lexes a CoffeeScript document.
pub fn lex(data: &str) -> Vec<Token> {
    let mut tokenizer = Tokenizer::new(data);
    let mut state_function = StateFunction(initial_state);
    loop {
        let StateFunction(actual_function) = state_function;
        match actual_function(&mut tokenizer) {
            Some(f) => state_function = f,
            None => {
                match tokenizer.states.pop() {
                    Some(f) => state_function = f,
                    None => return tokenizer.tokens(),
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::lex;
    use token::Token;
    use token::Category;

    #[test]
    fn it_works() {
        let data = include_str!("../../test_data/data.coffee");
        let tokens = lex(data);
        let expected_tokens = vec![
            Token{ lexeme: "class".to_string(), category: Category::Keyword },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "Coffee".to_string(), category: Category::Identifier },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "data".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "=".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "\"string\"".to_string(), category: Category::String },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "@data".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "=".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "'string'".to_string(), category: Category::String },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "key:".to_string(), category: Category::Literal },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "# comment".to_string(), category: Category::Comment },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "###\n  multi-line comment\n  ###".to_string(), category: Category::Comment },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "$".to_string(), category: Category::Keyword },
            Token{ lexeme: "(".to_string(), category: Category::Text },
            Token{ lexeme: "'.class'".to_string(), category: Category::String },
            Token{ lexeme: ")".to_string(), category: Category::Text },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }

    #[test]
    fn it_identifies_integers_and_operators() {
        let data = "123 + 456";
        let tokens = lex(data);
        let expected_tokens = vec![
            Token{ lexeme: "123".to_string(), category: Category::Integer },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "+".to_string(), category: Category::Operator },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "456".to_string(), category: Category::Integer },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }
}
<commit_msg>Break Coffeescript identifier tokens on open parentheses.<commit_after>//! A lexer for the CoffeeScript programming language.

use token::{Category, Token};
use tokenizer::{Tokenizer, StateFunction};

fn initial_state(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    if tokenizer.starts_with_lexeme("class") {
        tokenizer.tokenize_next(5, Category::Keyword);
        tokenizer.consume_whitespace();
        return Some(StateFunction(identifier))
    } else if tokenizer.starts_with_lexeme("if") {
        tokenizer.tokenize_next(2, Category::Keyword);
        return Some(StateFunction(whitespace))
    } else if tokenizer.starts_with_lexeme("else") {
        tokenizer.tokenize_next(4, Category::Keyword);
        return Some(StateFunction(whitespace))
    } else if tokenizer.starts_with_lexeme("return") {
        tokenizer.tokenize_next(6, Category::Keyword);
        return Some(StateFunction(whitespace))
    } else if tokenizer.starts_with_lexeme("true") {
        tokenizer.tokenize_next(4, Category::Boolean);
        return Some(StateFunction(initial_state))
    } else if tokenizer.starts_with_lexeme("false") {
        tokenizer.tokenize_next(5, Category::Boolean);
        return Some(StateFunction(initial_state))
    } else if tokenizer.starts_with_lexeme("var") {
        tokenizer.tokenize_next(3, Category::Keyword);
        tokenizer.consume_whitespace();
        return Some(StateFunction(identifier))
    } else if tokenizer.has_prefix("###") {
        tokenizer.tokenize(Category::Text);
        tokenizer.advance();
        tokenizer.advance();
        tokenizer.advance();
        return Some(StateFunction(multi_line_comment))
    }

    match tokenizer.current_char() {
        Some('"') => {
            tokenizer.tokenize(Category::Text);
            tokenizer.advance();
            Some(StateFunction(inside_string))
        },
        Some('\'') => {
            tokenizer.tokenize(Category::Text);
            tokenizer.advance();
            Some(StateFunction(inside_single_quote_string))
        },
        Some('.') => {
            tokenizer.tokenize_next(1, Category::Text);
            Some(StateFunction(initial_state))
        },
        Some('+') => {
            tokenizer.tokenize_next(1, Category::Operator);
            Some(StateFunction(initial_state))
        },
        Some(' ') | Some('\n') => {
            match tokenizer.next_non_whitespace_char() {
                Some('=') => {
                    tokenizer.tokenize(Category::Identifier);
                    tokenizer.consume_whitespace();
                    tokenizer.tokenize_next(1, Category::Text);
                },
                _ => tokenizer.consume_whitespace(),
            }
            Some(StateFunction(initial_state))
        },
        Some('=') => {
            tokenizer.tokenize(Category::Identifier);
            tokenizer.tokenize_next(1, Category::Text);
            Some(StateFunction(initial_state))
        },
        Some('#') => {
            tokenizer.tokenize(Category::Text);
            tokenizer.advance();
            Some(StateFunction(comment))
        },
        Some('$') => {
            tokenizer.tokenize_next(1, Category::Keyword);
            Some(StateFunction(initial_state))
        },
        Some('@') => {
            tokenizer.advance();
            Some(StateFunction(identifier))
        },
        Some(':') => {
            if tokenizer.starts_with_lexeme(":") {
                tokenizer.advance();
                tokenizer.tokenize(Category::Literal);
                tokenizer.consume_whitespace();
                Some(StateFunction(initial_state))
            } else {
                tokenizer.advance();
                Some(StateFunction(symbol))
            }
        },
        Some(c) => {
            tokenizer.advance();

            if c.is_numeric() {
                Some(StateFunction(integer))
            } else {
                Some(StateFunction(initial_state))
            }
        }

        None => {
            tokenizer.tokenize(Category::Text);
            None
        }
    }
}

fn inside_string(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                '"' => {
                    tokenizer.advance();
                    tokenizer.tokenize(Category::String);
                    Some(StateFunction(initial_state))
                },
                '\\' => {
                    tokenizer.advance();
                    tokenizer.advance();
                    Some(StateFunction(inside_string))
                }
                _ => {
                    tokenizer.advance();
                    Some(StateFunction(inside_string))
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::String);
            None
        }
    }
}

fn inside_single_quote_string(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                '\'' => {
                    tokenizer.advance();
                    tokenizer.tokenize(Category::String);
                    Some(StateFunction(initial_state))
                },
                '\\' => {
                    tokenizer.advance();
                    tokenizer.advance();
                    Some(StateFunction(inside_single_quote_string))
                }
                _ => {
                    tokenizer.advance();
                    Some(StateFunction(inside_single_quote_string))
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::String);
            None
        }
    }
}

fn whitespace(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                ' ' | '\n' => {
                    tokenizer.advance();
                    Some(StateFunction(whitespace))
                },
                _ => {
                    tokenizer.tokenize(Category::Whitespace);
                    match tokenizer.states.pop() {
                        Some(state) => Some(state),
                        None => Some(StateFunction(initial_state)),
                    }
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::Whitespace);
            None
        }
    }
}

fn identifier(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                ' ' | '\n' => {
                    tokenizer.tokenize(Category::Identifier);
                    Some(StateFunction(initial_state))
                },
                '|' | '(' | ')' | '-' => {
                    tokenizer.tokenize(Category::Identifier);
                    tokenizer.tokenize_next(1, Category::Text);
                    Some(StateFunction(initial_state))
                },
                _ => {
                    tokenizer.advance();
                    Some(StateFunction(identifier))
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::Identifier);
            None
        }
    }
}

fn comment(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            match c {
                '\n' => {
                    tokenizer.tokenize(Category::Comment);
                    Some(StateFunction(initial_state))
                },
                _ => {
                    tokenizer.advance();
                    Some(StateFunction(comment))
                }
            }
        }

        None => {
            tokenizer.tokenize(Category::Whitespace);
            None
        }
    }
}

fn multi_line_comment(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    if tokenizer.has_prefix("###") {
        tokenizer.advance();
        tokenizer.advance();
        tokenizer.advance();
        tokenizer.tokenize(Category::Comment);
        return Some(StateFunction(initial_state))
    }

    match tokenizer.current_char() {
        Some(_) => {
            tokenizer.advance();
            Some(StateFunction(multi_line_comment))
        },
        None => {
            tokenizer.tokenize(Category::Comment);
            None
        }
    }
}

fn integer(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            if c.is_numeric() {
                tokenizer.advance();
                Some(StateFunction(integer))
            } else {
                tokenizer.tokenize(Category::Integer);
                Some(StateFunction(initial_state))
            }
        }

        None => {
            tokenizer.tokenize(Category::Integer);
            None
        }
    }
}

fn symbol(tokenizer: &mut Tokenizer) -> Option<StateFunction> {
    match tokenizer.current_char() {
        Some(c) => {
            if c.is_alphanumeric() || c == '_' || c == '?' {
                tokenizer.advance();
                Some(StateFunction(symbol))
            } else {
                tokenizer.tokenize(Category::Literal);
                Some(StateFunction(initial_state))
            }
        }

        None => {
            tokenizer.tokenize(Category::Literal);
            None
        }
    }
}

/// Lexes a CoffeeScript document.
pub fn lex(data: &str) -> Vec<Token> {
    let mut tokenizer = Tokenizer::new(data);
    let mut state_function = StateFunction(initial_state);
    loop {
        let StateFunction(actual_function) = state_function;
        match actual_function(&mut tokenizer) {
            Some(f) => state_function = f,
            None => {
                match tokenizer.states.pop() {
                    Some(f) => state_function = f,
                    None => return tokenizer.tokens(),
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::lex;
    use token::Token;
    use token::Category;

    #[test]
    fn it_works() {
        let data = include_str!("../../test_data/data.coffee");
        let tokens = lex(data);
        let expected_tokens = vec![
            Token{ lexeme: "class".to_string(), category: Category::Keyword },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "Coffee".to_string(), category: Category::Identifier },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "data".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "=".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "\"string\"".to_string(), category: Category::String },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "@data".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "=".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "'string'".to_string(), category: Category::String },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "key:".to_string(), category: Category::Literal },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "# comment".to_string(), category: Category::Comment },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "###\n  multi-line comment\n  ###".to_string(), category: Category::Comment },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "$".to_string(), category: Category::Keyword },
            Token{ lexeme: "(".to_string(), category: Category::Text },
            Token{ lexeme: "'.class'".to_string(), category: Category::String },
            Token{ lexeme: ")".to_string(), category: Category::Text },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }

    #[test]
    fn it_identifies_integers_and_operators() {
        let data = "123 + 456";
        let tokens = lex(data);
        let expected_tokens = vec![
            Token{ lexeme: "123".to_string(), category: Category::Integer },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "+".to_string(), category: Category::Operator },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "456".to_string(), category: Category::Integer },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate termion;

use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::ffi::OsString;
use std::io::{stdin, stdout, BufRead, BufReader, Write};
use std::option::Option;
use std::path::Path;
use std::process::{Command, Stdio};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use std::vec::Vec;

use clap::{App, Arg};
use termion::event::{Event, Key, MouseEvent};
use termion::input::{MouseTerminal, TermRead};
use termion::raw::IntoRawMode;

const VERT_SEPARATOR: &'static str = "│";
const HORIZ_SEPARATOR: &'static str = "─";

#[derive(Debug, Clone)]
struct ListEntry {
    directory: String,
    file_name: String,
    line_number: u32,
    text: String
}

impl ListEntry {
    // format is: path/to/file.rs:123:line of text
    fn from_line(line: &str) -> Option<ListEntry> {
        let parts: Vec<&str> = line.splitn(3, ':').collect();

        if parts.len() != 3 {
            return None;
        }

        let path = Path::new(parts[0]);

        // FIXME: This cannot possibly be right. wtf.
        Some(ListEntry {
            directory: String::from(path.parent().unwrap().to_str().unwrap()),
            file_name: String::from(path.file_name().unwrap().to_str().unwrap()),
            line_number: parts[1].parse().unwrap(),
            text: String::from(parts[2]),
        })
    }
}

#[derive(Debug, Clone)]
struct FileList {
    entries: Vec<ListEntry>,
    current_index: Option<u32>,
}

impl FileList {
    fn new() -> FileList {
        FileList {
            entries: vec![],
            current_index: None
        }
    }

    fn add_entry(&mut self, entry: ListEntry) {
        // Insert the new element into it's sorted position
        let insert_pos = {
            let new_key = (&entry.directory, &entry.file_name, &entry.line_number);
            let result = self.entries.binary_search_by(|other| {
                let k = (&other.directory, &other.file_name, &other.line_number);
                return k.cmp(&new_key);
            });

            match result {
                Ok(pos) | Err(pos) => pos
            }
        };

        self.entries.insert(insert_pos, entry);

        if self.current_index.is_none() {
            self.current_index = Some(0);
        }
    }
}

#[derive(Debug)]
struct UI {
    list: FileList,
    file_cache: HashMap<(String, String), File>,
}

fn main() {
    let matches = App::new("dredge")
        .about("Search interactively")
        .arg(
            Arg::with_name("program")
                .long("program")
                .short("p")
                .value_name("PROGRAM")
                .default_value("ag")
                .help("grep or ag or w/e"),
        )
        .arg(
            Arg::with_name("query")
                .help("what to search for")
                .required(true),
        )
        .arg(
            Arg::with_name("directories")
                .help("where to search for it")
                .multiple(true)
                .default_value("."),
        )
        .arg(
            Arg::with_name("options")
                .help("options to pass directly to search program.")
                .multiple(true)
                .last(true),
        )
        .get_matches();

    let (tx, rx) = channel();

    thread::spawn(move || {
        run_command(tx).expect("failed to run command");
    });

    ui_loop(rx);
}

fn run_command(sender: Sender<ListEntry>) -> Result<(), Box<std::error::Error>> {
    let cmd = Command::new("ag")
        .args(["cmd", "src"].iter())
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    let output = cmd.stdout.unwrap();
    let reader = BufReader::new(output);

    for line in reader.lines() {
        if let Some(entry) = ListEntry::from_line(&line.unwrap()) {
            sender.send(entry)?;
        }
    }

    Ok(())
}

fn ui_loop(receiver: Receiver<ListEntry>) {
    let mut file_list = FileList::new();

    // FIXME: This should be mixed in with the event loop somehow.
    for entry in receiver.iter() {
        println!("entry => {:?}", entry);
        file_list.add_entry(entry);
    }

    let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
    let stdin = stdin();

    // write!(stdout, "{}{}", termion::clear::All, termion::cursor::Goto(1, 1)).unwrap();
    // stdout.flush().unwrap();

    for c in stdin.events() {
        match c.unwrap() {
            Event::Key(Key::Ctrl('c')) | Event::Key(Key::Char('q')) => break,

            Event::Mouse(me) => match me {
                MouseEvent::Press(_, x, y) => {
                    write!(stdout, "{}x", termion::cursor::Goto(x, y)).unwrap();
                }

                _ => (),
            },
            _ => {}
        }

        stdout.flush().unwrap();
    }
}
<commit_msg>Interleave search and terminal events<commit_after>extern crate clap;
extern crate termion;

use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::ffi::OsString;
use std::io::{stdin, stdout, BufRead, BufReader, Write};
use std::option::Option;
use std::path::Path;
use std::process::{Command, Stdio};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use std::vec::Vec;

use clap::{App, Arg};
use termion::event::{Event, Key, MouseEvent};
use termion::input::{MouseTerminal, TermRead};
use termion::raw::IntoRawMode;

const VERT_SEPARATOR: &'static str = "│";
const HORIZ_SEPARATOR: &'static str = "─";

enum ChanMessage {
    SearchResult(ListEntry),
    TerminalEvent(Event),
}

#[derive(Debug, Clone)]
struct ListEntry {
    directory: String,
    file_name: String,
    line_number: u32,
    text: String,
}

impl ListEntry {
    // format is: path/to/file.rs:123:line of text
    fn from_line(line: &str) -> Option<ListEntry> {
        let parts: Vec<&str> = line.splitn(3, ':').collect();

        if parts.len() != 3 {
            return None;
        }

        let path = Path::new(parts[0]);

        // FIXME: This cannot possibly be right. wtf.
        Some(ListEntry {
            directory: String::from(path.parent().unwrap().to_str().unwrap()),
            file_name: String::from(path.file_name().unwrap().to_str().unwrap()),
            line_number: parts[1].parse().unwrap(),
            text: String::from(parts[2]),
        })
    }
}

#[derive(Debug, Clone)]
struct FileList {
    entries: Vec<ListEntry>,
    current_index: Option<u32>,
}

impl FileList {
    fn new() -> FileList {
        FileList {
            entries: vec![],
            current_index: None,
        }
    }

    fn add_entry(&mut self, entry: ListEntry) {
        // Insert the new element into it's sorted position
        let insert_pos = {
            let new_key = (&entry.directory, &entry.file_name, &entry.line_number);
            let result = self.entries.binary_search_by(|other| {
                let k = (&other.directory, &other.file_name, &other.line_number);
                return k.cmp(&new_key);
            });

            match result {
                Ok(pos) | Err(pos) => pos,
            }
        };

        self.entries.insert(insert_pos, entry);

        if self.current_index.is_none() {
            self.current_index = Some(0);
        }
    }
}

#[derive(Debug)]
struct UI {
    list: FileList,
    file_cache: HashMap<(String, String), File>,
}

fn main() {
    let matches = App::new("dredge")
        .about("Search interactively")
        .arg(
            Arg::with_name("program")
                .long("program")
                .short("p")
                .value_name("PROGRAM")
                .default_value("ag")
                .help("grep or ag or w/e"),
        )
        .arg(
            Arg::with_name("query")
                .help("what to search for")
                .required(true),
        )
        .arg(
            Arg::with_name("directories")
                .help("where to search for it")
                .multiple(true)
                .default_value("."),
        )
        .arg(
            Arg::with_name("options")
                .help("options to pass directly to search program.")
                .multiple(true)
                .last(true),
        )
        .get_matches();

    let (tx, rx) = channel();

    let cmd_tx = tx.clone();
    let term_tx = tx.clone();

    thread::spawn(move || {
        run_command(cmd_tx).expect("failed to run command");
    });

    thread::spawn(move || {
        let stdin = stdin();

        for c in stdin.events() {
            term_tx
                .send(ChanMessage::TerminalEvent(c.unwrap()))
                .unwrap();
        }
    });

    ui_loop(rx);
}

fn run_command(sender: Sender<ChanMessage>) -> Result<(), Box<std::error::Error>> {
    let cmd = Command::new("ag")
        .args([".", "src"].iter())
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    let output = cmd.stdout.unwrap();
    let reader = BufReader::new(output);

    for line in reader.lines() {
        if let Some(entry) = ListEntry::from_line(&line.unwrap()) {
            sender.send(ChanMessage::SearchResult(entry))?;
        }
    }

    Ok(())
}

fn ui_loop(receiver: Receiver<ChanMessage>) -> Result<(), Box<std::error::Error>> {
    let mut file_list = FileList::new();

    let mut stdout = MouseTerminal::from(stdout().into_raw_mode()?);
    write!(
        stdout,
        "{}{}",
        termion::clear::All,
        termion::cursor::Goto(1, 1)
    )?;

    for entry in receiver.iter() {
        let mut redraw = false;

        match entry {
            ChanMessage::TerminalEvent(evt) => match evt {
                Event::Key(Key::Ctrl('c')) | Event::Key(Key::Char('q')) => break,
                Event::Mouse(me) => match me {
                    MouseEvent::Press(_, x, y) => {
                        write!(stdout, "{}x", termion::cursor::Goto(x, y))?;
                        stdout.flush()?;
                    }

                    _ => (),
                },

                _ => {}
            },

            ChanMessage::SearchResult(entry) => {
                redraw = true;
                file_list.add_entry(entry);
            }
        }

        if redraw {
            for (i, entry) in file_list.entries.iter().enumerate() {
                write!(
                    stdout,
                    "{}{}",
                    termion::cursor::Goto(1, 1 + i as u16),
                    entry.file_name
                )?;
                stdout.flush()?;
            }
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(deprecated)]

use prelude::v1::*;

use sys::fs::FileDesc;
use libc::{self, c_int, c_ulong};
use old_io::{self, IoResult, IoError};
use sys::c;
use sys_common;

pub struct TTY {
    pub fd: FileDesc,
}

#[cfg(any(target_os = "macos",
          target_os = "freebsd",
          target_os = "bitrig",
          target_os = "openbsd"))]
const TIOCGWINSZ: c_ulong = 0x40087468;

#[cfg(any(target_os = "linux", target_os = "android"))]
const TIOCGWINSZ: c_ulong = 0x00005413;

impl TTY {
    pub fn new(fd: c_int) -> IoResult<TTY> {
        if unsafe { libc::isatty(fd) } != 0 {
            Ok(TTY { fd: FileDesc::new(fd, true) })
        } else {
            Err(IoError {
                kind: old_io::MismatchedFileTypeForOperation,
                desc: "file descriptor is not a TTY",
                detail: None,
            })
        }
    }

    pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        self.fd.read(buf)
    }
    pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        self.fd.write(buf)
    }
    pub fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
        Err(sys_common::unimpl())
    }

    #[cfg(any(target_os = "linux",
              target_os = "android",
              target_os = "macos",
              target_os = "freebsd",
              target_os = "bitrig",
              target_os = "openbsd"))]
    pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
        unsafe {
            #[repr(C)]
            struct winsize {
                ws_row: u16,
                ws_col: u16,
                ws_xpixel: u16,
                ws_ypixel: u16
            }

            let mut size = winsize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 };
            if c::ioctl(self.fd.fd(), TIOCGWINSZ, &mut size) == -1 {
                Err(IoError {
                    kind: old_io::OtherIoError,
                    desc: "Size of terminal could not be determined",
                    detail: None,
                })
            } else {
                Ok((size.ws_col as int, size.ws_row as int))
            }
        }
    }

    #[cfg(any(target_os = "ios",
              target_os = "dragonfly"))]
    pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
        Err(sys_common::unimpl())
    }
}
<commit_msg>Implement `winsize()` for {ios,dragonfly}<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(deprecated)]

use prelude::v1::*;

use sys::fs::FileDesc;
use libc::{self, c_int, c_ulong};
use old_io::{self, IoResult, IoError};
use sys::c;
use sys_common;

pub struct TTY {
    pub fd: FileDesc,
}

#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "dragonfly",
          target_os = "freebsd",
          target_os = "bitrig",
          target_os = "openbsd"))]
const TIOCGWINSZ: c_ulong = 0x40087468;

#[cfg(any(target_os = "linux", target_os = "android"))]
const TIOCGWINSZ: c_ulong = 0x00005413;

impl TTY {
    pub fn new(fd: c_int) -> IoResult<TTY> {
        if unsafe { libc::isatty(fd) } != 0 {
            Ok(TTY { fd: FileDesc::new(fd, true) })
        } else {
            Err(IoError {
                kind: old_io::MismatchedFileTypeForOperation,
                desc: "file descriptor is not a TTY",
                detail: None,
            })
        }
    }

    pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        self.fd.read(buf)
    }
    pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        self.fd.write(buf)
    }
    pub fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
        Err(sys_common::unimpl())
    }

    pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
        unsafe {
            #[repr(C)]
            struct winsize {
                ws_row: u16,
                ws_col: u16,
                ws_xpixel: u16,
                ws_ypixel: u16
            }

            let mut size = winsize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 };
            if c::ioctl(self.fd.fd(), TIOCGWINSZ, &mut size) == -1 {
                Err(IoError {
                    kind: old_io::OtherIoError,
                    desc: "Size of terminal could not be determined",
                    detail: None,
                })
            } else {
                Ok((size.ws_col as int, size.ws_row as int))
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::process::{Child, Command};

pub struct Runner {
    process: Option<Child>,
    restart: bool,
    cls: bool,
}

impl Runner {
    pub fn new(restart: bool, clear: bool) -> Runner {
        Runner {
            process: None,
            restart: restart,
            cls: clear
        }
    }

    #[cfg(target_family = "windows")]
    fn clear(&self) {
        let _ = Command::new("cls").status();
    }

    #[cfg(target_family = "unix")]
    fn clear(&self) {
        let _ = Command::new("clear").status();
    }

    #[cfg(target_family = "windows")]
    fn kill(child: &mut Child) {
        let _ = child.kill();
    }

    #[cfg(target_family = "unix")]
    fn kill(child: &mut Child) {
        use libc;

        extern {
            fn killpg(pgrp: libc::pid_t, sig: libc::c_int) -> libc::c_int;
        }

        unsafe {
            killpg(child.id() as i32, libc::SIGTERM);
        }
    }

    #[cfg(target_family = "windows")]
    fn invoke(&self, cmd: &str, updated_paths: Vec<&str>) -> Option<Child> {
        let mut command = Command::new("cmd.exe");
        command.arg("/C").arg(cmd);

        if updated_paths.len() > 0 {
            command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
        }

        command.spawn().ok()
    }

    #[cfg(target_family = "unix")]
    fn invoke(&self, cmd: &str, updated_paths: Vec<&str>) -> Option<Child> {
        use libc;
        use std::os::unix::process::CommandExt;

        let mut command = Command::new("sh");
        command.arg("-c").arg(cmd);

        if updated_paths.is_empty() {
            command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
        }

        command
            .before_exec(|| unsafe { libc::setpgid(0, 0); Ok(()) })
            .spawn()
            .ok()
    }

    pub fn run_command(&mut self, cmd: &str, updated_paths: Vec<&str>) {
        if let Some(ref mut child) = self.process {
            if self.restart {
                debug!("Killing child process (pid: {})", child.id());
                Runner::kill(child);
            }

            debug!("Waiting for child process (pid: {})", child.id());
            Runner::wait(child);
        }

        if self.cls {
            self.clear();
        }

        debug!("Executing: {}", cmd);

        self.process = self.invoke(cmd, updated_paths);
    }

    #[cfg(target_family = "windows")]
    fn wait(child: &mut Child) {
        let _ = child.wait();
    }

    #[cfg(target_family = "unix")]
    fn wait(child: &mut Child) {
        use libc;

        unsafe {
            let pid = child.id() as i32;
            let status: Box<i32> = Box::new(0);
            libc::waitpid(-pid, Box::into_raw(status), 0);
        }
    }

}
<commit_msg>Fix silly error<commit_after>use std::process::{Child, Command};

pub struct Runner {
    process: Option<Child>,
    restart: bool,
    cls: bool,
}

impl Runner {
    pub fn new(restart: bool, clear: bool) -> Runner {
        Runner {
            process: None,
            restart: restart,
            cls: clear
        }
    }

    #[cfg(target_family = "windows")]
    fn clear(&self) {
        let _ = Command::new("cls").status();
    }

    #[cfg(target_family = "unix")]
    fn clear(&self) {
        let _ = Command::new("clear").status();
    }

    #[cfg(target_family = "windows")]
    fn kill(child: &mut Child) {
        let _ = child.kill();
    }

    #[cfg(target_family = "unix")]
    fn kill(child: &mut Child) {
        use libc;

        extern {
            fn killpg(pgrp: libc::pid_t, sig: libc::c_int) -> libc::c_int;
        }

        unsafe {
            killpg(child.id() as i32, libc::SIGTERM);
        }
    }

    #[cfg(target_family = "windows")]
    fn invoke(&self, cmd: &str, updated_paths: Vec<&str>) -> Option<Child> {
        let mut command = Command::new("cmd.exe");
        command.arg("/C").arg(cmd);

        if !updated_paths.is_empty() {
            command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
        }

        command.spawn().ok()
    }

    #[cfg(target_family = "unix")]
    fn invoke(&self, cmd: &str, updated_paths: Vec<&str>) -> Option<Child> {
        use libc;
        use std::os::unix::process::CommandExt;

        let mut command = Command::new("sh");
        command.arg("-c").arg(cmd);

        if !updated_paths.is_empty() {
            command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
        }

        command
            .before_exec(|| unsafe { libc::setpgid(0, 0); Ok(()) })
            .spawn()
            .ok()
    }

    pub fn run_command(&mut self, cmd: &str, updated_paths: Vec<&str>) {
        if let Some(ref mut child) = self.process {
            if self.restart {
                debug!("Killing child process (pid: {})", child.id());
                Runner::kill(child);
            }

            debug!("Waiting for child process (pid: {})", child.id());
            Runner::wait(child);
        }

        if self.cls {
            self.clear();
        }

        debug!("Executing: {}", cmd);

        self.process = self.invoke(cmd, updated_paths);
    }

    #[cfg(target_family = "windows")]
    fn wait(child: &mut Child) {
        let _ = child.wait();
    }

    #[cfg(target_family = "unix")]
    fn wait(child: &mut Child) {
        use libc;

        unsafe {
            let pid = child.id() as i32;
            let status: Box<i32> = Box::new(0);
            libc::waitpid(-pid, Box::into_raw(status), 0);
        }
    }

}
<|endoftext|>


<commit_before>/// Scoped thread.
///
/// # Examples
///
/// A basic scoped thread:
///
/// ```
/// crossbeam_utils::scoped::scope(|scope| {
///     scope.spawn(|| {
///         println!("Hello from a scoped thread!");
///     });
/// });
/// ```
///
/// When writing concurrent Rust programs, you'll sometimes see a pattern like this, using
/// [`std::thread::spawn`][spawn]:
///
/// ```ignore
/// let array = [1, 2, 3];
/// let mut guards = vec![];
///
/// for i in &array {
///     let guard = std::thread::spawn(move || {
///         println!("element: {}", i);
///     });
///
///     guards.push(guard);
/// }
///
/// for guard in guards {
///     guard.join().unwrap();
/// }
/// ```
///
/// The basic pattern is:
///
/// 1. Iterate over some collection.
/// 2. Spin up a thread to operate on each part of the collection.
/// 3. Join all the threads.
///
/// However, this code actually gives an error:
///
/// ```text
/// error: `array` does not live long enough
/// for i in &array {
///           ^~~~~
/// in expansion of for loop expansion
/// note: expansion site
/// note: reference must be valid for the static lifetime...
/// note: ...but borrowed value is only valid for the block suffix following statement 0 at ...
///     let array = [1, 2, 3];
///     let mut guards = vec![];
///
///     for i in &array {
///         let guard = std::thread::spawn(move || {
///             println!("element: {}", i);
/// ...
/// error: aborting due to previous error
/// ```
///
/// Because [`std::thread::spawn`][spawn] doesn't know about this scope, it requires a
/// `'static` lifetime. One way of giving it a proper lifetime is to use an [`Arc`][arc]:
///
/// [arc]: http://doc.rust-lang.org/stable/std/sync/struct.Arc.html
///
/// ```
/// use std::sync::Arc;
///
/// let array = Arc::new([1, 2, 3]);
/// let mut guards = vec![];
///
/// for i in 0..array.len() {
///     let a = array.clone();
///
///     let guard = std::thread::spawn(move || {
///         println!("element: {}", a[i]);
///     });
///
///     guards.push(guard);
/// }
///
/// for guard in guards {
///     guard.join().unwrap();
/// }
/// ```
///
/// But this introduces unnecessary allocation, as `Arc<T>` puts its data on the heap, and we
/// also end up dealing with reference counts. We know that we're joining the threads before
/// our function returns, so just taking a reference _should_ be safe. Rust can't know that,
/// though.
///
/// Enter scoped threads. Here's our original example, using `spawn` from crossbeam rather
/// than from `std::thread`:
///
/// ```
/// let array = [1, 2, 3];
///
/// crossbeam_utils::scoped::scope(|scope| {
///     for i in &array {
///         scope.spawn(move || {
///             println!("element: {}", i);
///         });
///     }
/// });
/// ```
///
/// Much more straightforward.
// FIXME(jeehoonkang): maybe we should create a new crate for scoped threads.

use std::cell::RefCell;
use std::fmt;
use std::mem;
use std::rc::Rc;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use std::io;

use atomic_option::AtomicOption;

#[doc(hidden)]
trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce()> FnBox for F {
    fn call_box(self: Box<Self>) {
        (*self)()
    }
}

/// Like `std::thread::spawn`, but without the closure bounds.
pub unsafe fn spawn_unsafe<'a, F>(f: F) -> thread::JoinHandle<()>
where
    F: FnOnce() + Send + 'a,
{
    let builder = thread::Builder::new();
    builder_spawn_unsafe(builder, f).unwrap()
}

/// Like `std::thread::Builder::spawn`, but without the closure bounds.
pub unsafe fn builder_spawn_unsafe<'a, F>(
    builder: thread::Builder,
    f: F,
) -> io::Result<thread::JoinHandle<()>>
where
    F: FnOnce() + Send + 'a,
{
    use std::mem;

    let closure: Box<FnBox + 'a> = Box::new(f);
    let closure: Box<FnBox + Send> = mem::transmute(closure);
    builder.spawn(move || closure.call_box())
}

pub struct Scope<'a> {
    dtors: RefCell<Option<DtorChain<'a>>>,
}

struct DtorChain<'a> {
    dtor: Box<FnBox + 'a>,
    next: Option<Box<DtorChain<'a>>>,
}

enum JoinState {
    Running(thread::JoinHandle<()>),
    Joined,
}

impl JoinState {
    fn join(&mut self) {
        let mut state = JoinState::Joined;
        mem::swap(self, &mut state);
        if let JoinState::Running(handle) = state {
            let res = handle.join();

            if !thread::panicking() {
                res.unwrap();
            }
        }
    }
}

/// A handle to a scoped thread
pub struct ScopedJoinHandle<T> {
    inner: Rc<RefCell<JoinState>>,
    packet: Arc<AtomicOption<T>>,
    thread: thread::Thread,
}

/// Create a new `scope`, for deferred destructors.
///
/// Scopes, in particular, support [*scoped thread spawning*](struct.Scope.html#method.spawn).
///
/// # Examples
///
/// Creating and using a scope:
///
/// ```
/// crossbeam_utils::scoped::scope(|scope| {
///     scope.defer(|| println!("Exiting scope"));
///     scope.spawn(|| println!("Running child thread in scope"))
/// });
/// // Prints messages in the reverse order written
/// ```
pub fn scope<'a, F, R>(f: F) -> R
where
    F: FnOnce(&Scope<'a>) -> R,
{
    let mut scope = Scope { dtors: RefCell::new(None) };
    let ret = f(&scope);
    scope.drop_all();
    ret
}

impl<'a> fmt::Debug for Scope<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Scope {{ ... }}")
    }
}

impl<T> fmt::Debug for ScopedJoinHandle<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ScopedJoinHandle {{ ... }}")
    }
}

impl<'a> Scope<'a> {
    // This method is carefully written in a transactional style, so
    // that it can be called directly and, if any dtor panics, can be
    // resumed in the unwinding this causes. By initially running the
    // method outside of any destructor, we avoid any leakage problems
    // due to @rust-lang/rust#14875.
    fn drop_all(&mut self) {
        loop {
            // use a separate scope to ensure that the RefCell borrow
            // is relinquished before running `dtor`
            let dtor = {
                let mut dtors = self.dtors.borrow_mut();
                if let Some(mut node) = dtors.take() {
                    *dtors = node.next.take().map(|b| *b);
                    node.dtor
                } else {
                    return;
                }
            };
            dtor.call_box()
        }
    }

    /// Schedule code to be executed when exiting the scope.
    ///
    /// This is akin to having a destructor on the stack, except that it is
    /// *guaranteed* to be run.
    pub fn defer<F>(&self, f: F)
    where
        F: FnOnce() + 'a,
    {
        let mut dtors = self.dtors.borrow_mut();
        *dtors = Some(DtorChain {
            dtor: Box::new(f),
            next: dtors.take().map(Box::new),
        });
    }

    /// Create a scoped thread.
    ///
    /// `spawn` is similar to the [`spawn`][spawn] function in Rust's standard library. The
    /// difference is that this thread is scoped, meaning that it's guaranteed to terminate
    /// before the current stack frame goes away, allowing you to reference the parent stack frame
    /// directly. This is ensured by having the parent thread join on the child thread before the
    /// scope exits.
    ///
    /// [spawn]: http://doc.rust-lang.org/std/thread/fn.spawn.html
    pub fn spawn<F, T>(&self, f: F) -> ScopedJoinHandle<T>
    where
        F: FnOnce() -> T + Send + 'a,
        T: Send + 'a,
    {
        self.builder().spawn(f).unwrap()
    }

    /// Generates the base configuration for spawning a scoped thread, from which configuration
    /// methods can be chained.
    pub fn builder<'s>(&'s self) -> ScopedThreadBuilder<'s, 'a> {
        ScopedThreadBuilder {
            scope: self,
            builder: thread::Builder::new(),
        }
    }
}

/// Scoped thread configuration. Provides detailed control over the properties and behavior of new
/// scoped threads.
pub struct ScopedThreadBuilder<'s, 'a: 's> {
    scope: &'s Scope<'a>,
    builder: thread::Builder,
}

impl<'s, 'a: 's> ScopedThreadBuilder<'s, 'a> {
    /// Names the thread-to-be. Currently the name is used for identification only in panic
    /// messages.
    pub fn name(mut self, name: String) -> ScopedThreadBuilder<'s, 'a> {
        self.builder = self.builder.name(name);
        self
    }

    /// Sets the size of the stack for the new thread.
    pub fn stack_size(mut self, size: usize) -> ScopedThreadBuilder<'s, 'a> {
        self.builder = self.builder.stack_size(size);
        self
    }

    /// Spawns a new thread, and returns a join handle for it.
    pub fn spawn<F, T>(self, f: F) -> io::Result<ScopedJoinHandle<T>>
    where
        F: FnOnce() -> T + Send + 'a,
        T: Send + 'a,
    {
        let their_packet = Arc::new(AtomicOption::new());
        let my_packet = their_packet.clone();

        let join_handle = try!(unsafe {
            builder_spawn_unsafe(self.builder, move || {
                their_packet.swap(f(), Ordering::Relaxed);
            })
        });

        let thread = join_handle.thread().clone();
        let deferred_handle = Rc::new(RefCell::new(JoinState::Running(join_handle)));
        let my_handle = deferred_handle.clone();

        self.scope.defer(move || {
            let mut state = deferred_handle.borrow_mut();
            state.join();
        });

        Ok(ScopedJoinHandle {
            inner: my_handle,
            packet: my_packet,
            thread: thread,
        })
    }
}

impl<T> ScopedJoinHandle<T> {
    /// Join the scoped thread, returning the result it produced.
    pub fn join(self) -> T {
        self.inner.borrow_mut().join();
        self.packet.take(Ordering::Relaxed).unwrap()
    }

    /// Get the underlying thread handle.
    pub fn thread(&self) -> &thread::Thread {
        &self.thread
    }
}

impl<'a> Drop for Scope<'a> {
    fn drop(&mut self) {
        self.drop_all()
    }
}
<commit_msg>Add join for scoped thread API (#7)<commit_after>/// Scoped thread.
///
/// # Examples
///
/// A basic scoped thread:
///
/// ```
/// crossbeam_utils::scoped::scope(|scope| {
///     scope.spawn(|| {
///         println!("Hello from a scoped thread!");
///     });
/// });
/// ```
///
/// When writing concurrent Rust programs, you'll sometimes see a pattern like this, using
/// [`std::thread::spawn`][spawn]:
///
/// ```ignore
/// let array = [1, 2, 3];
/// let mut guards = vec![];
///
/// for i in &array {
///     let guard = std::thread::spawn(move || {
///         println!("element: {}", i);
///     });
///
///     guards.push(guard);
/// }
///
/// for guard in guards {
///     guard.join().unwrap();
/// }
/// ```
///
/// The basic pattern is:
///
/// 1. Iterate over some collection.
/// 2. Spin up a thread to operate on each part of the collection.
/// 3. Join all the threads.
///
/// However, this code actually gives an error:
///
/// ```text
/// error: `array` does not live long enough
/// for i in &array {
///           ^~~~~
/// in expansion of for loop expansion
/// note: expansion site
/// note: reference must be valid for the static lifetime...
/// note: ...but borrowed value is only valid for the block suffix following statement 0 at ...
///     let array = [1, 2, 3];
///     let mut guards = vec![];
///
///     for i in &array {
///         let guard = std::thread::spawn(move || {
///             println!("element: {}", i);
/// ...
/// error: aborting due to previous error
/// ```
///
/// Because [`std::thread::spawn`][spawn] doesn't know about this scope, it requires a
/// `'static` lifetime. One way of giving it a proper lifetime is to use an [`Arc`][arc]:
///
/// [arc]: http://doc.rust-lang.org/stable/std/sync/struct.Arc.html
///
/// ```
/// use std::sync::Arc;
///
/// let array = Arc::new([1, 2, 3]);
/// let mut guards = vec![];
///
/// for i in 0..array.len() {
///     let a = array.clone();
///
///     let guard = std::thread::spawn(move || {
///         println!("element: {}", a[i]);
///     });
///
///     guards.push(guard);
/// }
///
/// for guard in guards {
///     guard.join().unwrap();
/// }
/// ```
///
/// But this introduces unnecessary allocation, as `Arc<T>` puts its data on the heap, and we
/// also end up dealing with reference counts. We know that we're joining the threads before
/// our function returns, so just taking a reference _should_ be safe. Rust can't know that,
/// though.
///
/// Enter scoped threads. Here's our original example, using `spawn` from crossbeam rather
/// than from `std::thread`:
///
/// ```
/// let array = [1, 2, 3];
///
/// crossbeam_utils::scoped::scope(|scope| {
///     for i in &array {
///         scope.spawn(move || {
///             println!("element: {}", i);
///         });
///     }
/// });
/// ```
///
/// Much more straightforward.
// FIXME(jeehoonkang): maybe we should create a new crate for scoped threads.

use std::cell::RefCell;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ops::DerefMut;
use std::rc::Rc;
use std::thread;
use std::io;

#[doc(hidden)]
trait FnBox<T> {
    fn call_box(self: Box<Self>) -> T;
}

impl<T, F: FnOnce() -> T> FnBox<T> for F {
    fn call_box(self: Box<Self>) -> T {
        (*self)()
    }
}

/// Like `std::thread::spawn`, but without the closure bounds.
pub unsafe fn spawn_unsafe<'a, F>(f: F) -> thread::JoinHandle<()>
where
    F: FnOnce() + Send + 'a,
{
    let builder = thread::Builder::new();
    builder_spawn_unsafe(builder, f).unwrap()
}

/// Like `std::thread::Builder::spawn`, but without the closure bounds.
pub unsafe fn builder_spawn_unsafe<'a, F>(
    builder: thread::Builder,
    f: F,
) -> io::Result<thread::JoinHandle<()>>
where
    F: FnOnce() + Send + 'a,
{
    let closure: Box<FnBox<()> + 'a> = Box::new(f);
    let closure: Box<FnBox<()> + Send> = mem::transmute(closure);
    builder.spawn(move || closure.call_box())
}

pub struct Scope<'a> {
    /// The list of the deferred functions and thread join jobs.
    dtors: RefCell<Option<DtorChain<'a, ()>>>,
    // !Send + !Sync
    _marker: PhantomData<*const ()>,
}

struct DtorChain<'a, T> {
    dtor: Box<FnBox<T> + 'a>,
    next: Option<Box<DtorChain<'a, T>>>,
}

impl<'a, T> DtorChain<'a, T> {
    pub fn pop(chain: &mut Option<DtorChain<'a, T>>) -> Option<Box<FnBox<T> + 'a>> {
        chain.take().map(|mut node| {
            *chain = node.next.take().map(|b| *b);
            node.dtor
        })
    }
}

struct JoinState<T> {
    join_handle: thread::JoinHandle<()>,
    result: usize,
    _marker: PhantomData<T>,
}

impl<T: Send> JoinState<T> {
    fn new(join_handle: thread::JoinHandle<()>, result: usize) -> JoinState<T> {
        JoinState {
            join_handle: join_handle,
            result: result,
            _marker: PhantomData,
        }
    }

    fn join(self) -> thread::Result<T> {
        let result = self.result;
        self.join_handle.join().map(|_| {
            unsafe { *Box::from_raw(result as *mut T) }
        })
    }
}

/// A handle to a scoped thread
pub struct ScopedJoinHandle<'a, T: 'a> {
    // !Send + !Sync
    inner: Rc<RefCell<Option<JoinState<T>>>>,
    thread: thread::Thread,
    _marker: PhantomData<&'a T>,
}

/// Create a new `scope`, for deferred destructors.
///
/// Scopes, in particular, support [*scoped thread spawning*](struct.Scope.html#method.spawn).
///
/// # Examples
///
/// Creating and using a scope:
///
/// ```
/// crossbeam_utils::scoped::scope(|scope| {
///     scope.defer(|| println!("Exiting scope"));
///     scope.spawn(|| println!("Running child thread in scope"))
/// });
/// // Prints messages in the reverse order written
/// ```
///
/// # Panics
///
/// `scoped::scope()` panics if a spawned thread panics but it is not joined inside the scope.
pub fn scope<'a, F, R>(f: F) -> R
where
    F: FnOnce(&Scope<'a>) -> R,
{
    let mut scope = Scope {
        dtors: RefCell::new(None),
        _marker: PhantomData,
    };
    let ret = f(&scope);
    scope.drop_all();
    ret
}

impl<'a> fmt::Debug for Scope<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Scope {{ ... }}")
    }
}

impl<'a, T> fmt::Debug for ScopedJoinHandle<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ScopedJoinHandle {{ ... }}")
    }
}

impl<'a> Scope<'a> {
    // This method is carefully written in a transactional style, so
    // that it can be called directly and, if any dtor panics, can be
    // resumed in the unwinding this causes. By initially running the
    // method outside of any destructor, we avoid any leakage problems
    // due to @rust-lang/rust#14875.
    fn drop_all(&mut self) {
        while let Some(dtor) = DtorChain::pop(&mut self.dtors.borrow_mut()) {
            dtor.call_box();
        }
    }

    /// Schedule code to be executed when exiting the scope.
    ///
    /// This is akin to having a destructor on the stack, except that it is
    /// *guaranteed* to be run. It is guaranteed that the function is called
    /// after all the spawned threads are joined.
    pub fn defer<F>(&self, f: F)
    where
        F: FnOnce() + 'a,
    {
        let mut dtors = self.dtors.borrow_mut();
        *dtors = Some(DtorChain {
            dtor: Box::new(f),
            next: dtors.take().map(Box::new),
        });
    }

    /// Create a scoped thread.
    ///
    /// `spawn` is similar to the [`spawn`][spawn] function in Rust's standard library. The
    /// difference is that this thread is scoped, meaning that it's guaranteed to terminate
    /// before the current stack frame goes away, allowing you to reference the parent stack frame
    /// directly. This is ensured by having the parent thread join on the child thread before the
    /// scope exits.
    ///
    /// [spawn]: http://doc.rust-lang.org/std/thread/fn.spawn.html
    pub fn spawn<'s, F, T>(&'s self, f: F) -> ScopedJoinHandle<'a, T>
    where
        'a: 's,
        F: FnOnce() -> T + Send + 'a,
        T: Send + 'a,
    {
        self.builder().spawn(f).unwrap()
    }

    /// Generates the base configuration for spawning a scoped thread, from which configuration
    /// methods can be chained.
    pub fn builder<'s>(&'s self) -> ScopedThreadBuilder<'s, 'a> {
        ScopedThreadBuilder {
            scope: self,
            builder: thread::Builder::new(),
        }
    }
}

/// Scoped thread configuration. Provides detailed control over the properties and behavior of new
/// scoped threads.
pub struct ScopedThreadBuilder<'s, 'a: 's> {
    scope: &'s Scope<'a>,
    builder: thread::Builder,
}

impl<'s, 'a: 's> ScopedThreadBuilder<'s, 'a> {
    /// Names the thread-to-be. Currently the name is used for identification only in panic
    /// messages.
    pub fn name(mut self, name: String) -> ScopedThreadBuilder<'s, 'a> {
        self.builder = self.builder.name(name);
        self
    }

    /// Sets the size of the stack for the new thread.
    pub fn stack_size(mut self, size: usize) -> ScopedThreadBuilder<'s, 'a> {
        self.builder = self.builder.stack_size(size);
        self
    }

    /// Spawns a new thread, and returns a join handle for it.
    pub fn spawn<F, T>(self, f: F) -> io::Result<ScopedJoinHandle<'a, T>>
    where
        F: FnOnce() -> T + Send + 'a,
        T: Send + 'a,
    {
        // The `Box` constructed below is written only by the spawned thread,
        // and read by the current thread only after the spawned thread is
        // joined (`JoinState::join()`). Thus there are no data races.
        let result = Box::into_raw(Box::<T>::new(unsafe { mem::uninitialized() })) as usize;

        let join_handle = try!(unsafe {
            builder_spawn_unsafe(self.builder, move || {
                let mut result = Box::from_raw(result as *mut T);
                *result = f();
                mem::forget(result);
            })
        });
        let thread = join_handle.thread().clone();

        let join_state = JoinState::<T>::new(join_handle, result);
        let deferred_handle = Rc::new(RefCell::new(Some(join_state)));
        let my_handle = deferred_handle.clone();

        self.scope.defer(move || {
            let state = mem::replace(deferred_handle.borrow_mut().deref_mut(), None);
            if let Some(state) = state {
                state.join().unwrap();
            }
        });

        Ok(ScopedJoinHandle {
            inner: my_handle,
            thread: thread,
            _marker: PhantomData,
        })
    }
}

impl<'a, T: Send + 'a> ScopedJoinHandle<'a, T> {
    /// Join the scoped thread, returning the result it produced.
    pub fn join(self) -> thread::Result<T> {
        let state = mem::replace(self.inner.borrow_mut().deref_mut(), None);
        state.unwrap().join()
    }

    /// Get the underlying thread handle.
    pub fn thread(&self) -> &thread::Thread {
        &self.thread
    }
}

impl<'a> Drop for Scope<'a> {
    fn drop(&mut self) {
        self.drop_all()
    }
}
<|endoftext|>


<commit_before>pub use lang::Lang;

#[derive(PartialEq, Eq, Debug)]
pub enum Script {
   Cyrillic,
   Latin,
   CMN,
   KAT,
}

pub fn detect_script(text : String) -> Option<Script> {
   let mut latin_count = 0;
   let mut cyrillic_count = 0;

   for ch in text.chars() {
       if Script::Cyrillic.has_char(ch) { cyrillic_count += 1; }
       if Script::Latin.has_char(ch) {  latin_count += 1; }
   }

   if latin_count > 0 && latin_count > cyrillic_count {
       Some(Script::Latin)
   } else if cyrillic_count > 0 && cyrillic_count > latin_count {
       Some(Script::Cyrillic)
   } else {
       None
   }
}

// TODO: Use http://jrgraphix.net/research/unicode_blocks.php
impl Script {
   pub fn has_char(&self, ch: char) -> bool {
       match *self {
           Script::Cyrillic => is_cyrillic(ch),
           Script::Latin => is_latin(ch),
           Script::CMN => is_cmn(ch),
           Script::KAT => is_kat(ch)
       }
   }
}

fn is_cyrillic(ch: char) -> bool {
   match ch {
       '\u{0400}'...'\u{0484}' |
       '\u{0487}'...'\u{052F}' |
       '\u{2DE0}'...'\u{2DFF}' |
       '\u{A640}'...'\u{A69D}' |
       '\u{1D2B}' |
       '\u{1D78}' |
       '\u{A69F}' => true,
       _ => false
   }
}

// TODO: finish implementation, with all the Latin chars.
fn is_latin(ch : char) -> bool {
   match ch {
       'a'...'z' | 'A'...'Z' => true,
       _ => false
   }
}

// TODO: implement
fn is_cmn(ch : char) -> bool {
   ch == 'x'
}

// Is Georgian char?
fn is_kat(ch : char) -> bool {
   match ch {
       '\u{10A0}'...'\u{10FF}' => true,
       _ => false
   }
}

#[cfg(test)]
mod tests {
   use super::Script;
   use super::is_cyrillic;
   use super::is_latin;
   use super::is_kat;
   use super::detect_script;

   #[test]
   fn test_detect_script() {
       assert_eq!(detect_script("1234567890-,;!".to_string()), None);
       assert_eq!(detect_script("Hello!".to_string()), Some(Script::Latin));
       assert_eq!(detect_script("Привет всем!".to_string()), Some(Script::Cyrillic));
       assert_eq!(detect_script("Привет всем! Этот текст на русском with some English.".to_string()), Some(Script::Cyrillic));
       assert_eq!(detect_script("Russian word любовь means love.".to_string()), Some(Script::Latin));
   }

   #[test]
   fn test_is_cyrillic() {
       assert_eq!(is_cyrillic('а'), true);
       assert_eq!(is_cyrillic('Я'), true);
       assert_eq!(is_cyrillic('Ґ'), true);
       assert_eq!(is_cyrillic('ї'), true);
       assert_eq!(is_cyrillic('Ꙕ'), true);

       assert_eq!(is_cyrillic('L'), false);
   }

   #[test]
   fn test_is_latin() {
       assert_eq!(is_latin('z'), true);
       assert_eq!(is_latin('A'), true);

       assert_eq!(is_latin('ж'), false);
   }

   #[test]
   fn test_is_kat() {
       assert_eq!(is_kat('რ'), true);
       assert_eq!(is_kat('ж'), false);
   }
}
<commit_msg>Improve detect_script function<commit_after>pub use lang::Lang;

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Script {
    Cyrillic,
    Latin,
    Cmn,
    Kat,
}

static FUNCS : &'static [(Script, fn(char) -> bool)] = &[
    (Script::Cyrillic, is_cyrillic),
    (Script::Latin   , is_latin),
    (Script::Kat     , is_kat),
    (Script::Cmn     , is_cmn)
];

pub fn detect_script(text : String) -> Option<Script> {
    let mut max_score = 0;
    let mut result = None;

    for &(script, func) in FUNCS {
        let mut current_score = 0;
        for ch in text.chars() {
            if func(ch) { current_score += 1; }
        }
        if current_score > max_score {
            max_score = current_score;
            result = Some(script);
        }
    }

    result
}

// TODO: Use http://jrgraphix.net/research/unicode_blocks.php
fn is_cyrillic(ch: char) -> bool {
   match ch {
       '\u{0400}'...'\u{0484}' |
       '\u{0487}'...'\u{052F}' |
       '\u{2DE0}'...'\u{2DFF}' |
       '\u{A640}'...'\u{A69D}' |
       '\u{1D2B}' |
       '\u{1D78}' |
       '\u{A69F}' => true,
       _ => false
   }
}

// TODO: finish implementation, with all the Latin chars.
fn is_latin(ch : char) -> bool {
   match ch {
       'a'...'z' | 'A'...'Z' => true,
       _ => false
   }
}

// Is Georgian char?
fn is_kat(ch : char) -> bool {
   match ch {
       '\u{10A0}'...'\u{10FF}' => true,
       _ => false
   }
}

// TODO: likely not the full set of possible chars..
fn is_cmn(ch : char) -> bool {
    match ch {
        '\u{2E80}'...'\u{2E99}' |
        '\u{2E9B}'...'\u{2EF3}' |
        '\u{2F00}'...'\u{2FD5}' |
        '\u{3005}' |
        '\u{3007}' |
        '\u{3021}'...'\u{3029}' |
        '\u{3038}'...'\u{303B}' |
        '\u{3400}'...'\u{4DB5}' |
        '\u{4E00}'...'\u{9FCC}' |
        '\u{F900}'...'\u{FA6D}' |
        '\u{FA70}'...'\u{FAD9}' => true,
        _ => false
    }
}

#[cfg(test)]
mod tests {
   use super::Script;
   use super::is_cyrillic;
   use super::is_latin;
   use super::is_kat;
   use super::detect_script;

   #[test]
   fn test_detect_script() {
       assert_eq!(detect_script("1234567890-,;!".to_string()), None);

       // One script
       assert_eq!(detect_script("Hello!".to_string()), Some(Script::Latin));
       assert_eq!(detect_script("Привет всем!".to_string()), Some(Script::Cyrillic));
       assert_eq!(detect_script("ქართული ენა მსოფლიო ენების კლასიფიკაციაში".to_string()), Some(Script::Kat));
       assert_eq!(detect_script("県見夜上温国阪題富販重点惑碁川写陸周。乗著渡営負励策決保見現稿".to_string()), Some(Script::Cmn));

       // Mixed scripts
       assert_eq!(detect_script("Привет всем! Этот текст на русском with some English.".to_string()), Some(Script::Cyrillic));
       assert_eq!(detect_script("Russian word любовь means love.".to_string()), Some(Script::Latin));
   }

   #[test]
   fn test_is_cyrillic() {
       assert_eq!(is_cyrillic('а'), true);
       assert_eq!(is_cyrillic('Я'), true);
       assert_eq!(is_cyrillic('Ґ'), true);
       assert_eq!(is_cyrillic('ї'), true);
       assert_eq!(is_cyrillic('Ꙕ'), true);

       assert_eq!(is_cyrillic('L'), false);
   }

   #[test]
   fn test_is_latin() {
       assert_eq!(is_latin('z'), true);
       assert_eq!(is_latin('A'), true);

       assert_eq!(is_latin('ж'), false);
   }

   #[test]
   fn test_is_kat() {
       assert_eq!(is_kat('რ'), true);
       assert_eq!(is_kat('ж'), false);
   }
}
<|endoftext|>