<commit_before>use std::cmp::{max, min};

use crate::tokenizer;
use crate::{
    constants::*,
    token_collector::{Style, StyledToken, TokenCollector},
};
use diffus::{
    edit::{self, collection},
    Diffable,
};

/// If more than this percentage of either adds or moves is highlighted, we
/// consider it to be a replacement rather than a move, and skip highlighting
/// it.
const MAX_HIGHLIGHT_PERCENTAGE: usize = 30;

const LARGE_COUNT_CHANGE_PERCENT: usize = 100;
const SMALL_COUNT_CHANGE: usize = 10;

/// Like format!(), but faster for our special case
fn format_simple_line(old_new: &str, plus_minus: char, contents: &str) -> String {
    let mut line = String::with_capacity(old_new.len() + 1 + contents.len() + NORMAL.len());
    line.push_str(old_new);
    line.push(plus_minus);
    line.push_str(contents);
    line.push_str(NORMAL);
    return line;
}

/// Format old and new lines in OLD and NEW colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format(old_text: &str, new_text: &str) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for old_line in old_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        lines.push(format_simple_line(OLD, '-', old_line));
    }
    if (!old_text.is_empty()) && !old_text.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in new_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        lines.push(format_simple_line(NEW, '+', add_line));
    }
    if (!new_text.is_empty()) && !new_text.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

/// Check whether old or new has the least amount of lines.
///
/// Take the shortest one and try diffing it against both the start and the end
/// of the longer one.
///
/// Check some goodness solution for both flavors and go for the best one.
#[must_use]
fn partial_format(old_text: &str, new_text: &str) -> Vec<String> {
    return simple_format(old_text, new_text);
}

/// Returns a vector of ANSI highlighted lines
#[must_use]
pub fn format(old_text: &str, new_text: &str) -> Vec<String> {
    if old_text.is_empty() || new_text.is_empty() {
        return simple_format(old_text, new_text);
    }

    // These checks make us faster, please use the benchmark.py script before
    // and after if you change this.
    if is_large_newline_count_change(old_text, new_text) {
        return partial_format(old_text, new_text);
    }
    if is_large_byte_count_change(old_text, new_text) {
        return simple_format(old_text, new_text);
    }

    // Find diffs between adds and removals
    let mut old_collector = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
    let mut new_collector = TokenCollector::create(StyledToken::new("+".to_string(), Style::New));

    // Tokenize adds and removes before diffing them
    let tokenized_old = tokenizer::tokenize(old_text);
    let tokenized_new = tokenizer::tokenize(new_text);

    let diff = tokenized_old.diff(&tokenized_new);
    match diff {
        edit::Edit::Copy(unchanged) => {
            for token in unchanged {
                old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                new_collector.push(StyledToken::new(token.to_string(), Style::New));
            }
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        collection::Edit::Copy(token) => {
                            old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                            new_collector.push(StyledToken::new(token.to_string(), Style::New));
                        }
                        collection::Edit::Insert(token) => {
                            if *token == "\n" {
                                // Make sure the highlighted linefeed is visible
                                new_collector
                                    .push(StyledToken::new("⏎".to_string(), Style::NewInverse));
                            }
                            new_collector
                                .push(StyledToken::new(token.to_string(), Style::NewInverse));
                        }
                        collection::Edit::Remove(token) => {
                            if *token == "\n" {
                                // Make sure the highlighted linefeed is visible
                                old_collector
                                    .push(StyledToken::new("⏎".to_string(), Style::OldInverse));
                            }
                            old_collector
                                .push(StyledToken::new(token.to_string(), Style::OldInverse));
                        }
                        collection::Edit::Change(_) => unimplemented!("Not implemented, help!"),
                    };
                })
                .for_each(drop);
        }
    }

    let highlighted_old_text = old_collector.render();
    let highlighted_new_text = new_collector.render();

    let highlighted_bytes_count =
        old_collector.highlighted_chars_count() + new_collector.highlighted_chars_count();
    let bytes_count = old_collector.chars_count() + new_collector.chars_count();

    // Don't highlight too much
    if (100 * highlighted_bytes_count) / bytes_count > MAX_HIGHLIGHT_PERCENTAGE {
        return partial_format(old_text, new_text);
    }

    return to_lines(&highlighted_old_text, &highlighted_new_text);
}

#[must_use]
fn is_large_count_change(count1: usize, count2: usize) -> bool {
    // This check makes us ignore some changes, thus making us faster. Please
    // use the benchmark.py script before and after if you touch this code.

    let high_count = max(count1, count2);
    let low_count = min(count1, count2);

    if high_count - low_count <= SMALL_COUNT_CHANGE {
        return false;
    }

    // "+ 99" makes the result round up, so 0->0, 1->2.
    let low_count_plus_percentage = (low_count * (LARGE_COUNT_CHANGE_PERCENT + 100) + 99) / 100;

    return high_count >= low_count_plus_percentage;
}

#[must_use]
fn is_large_byte_count_change(old_text: &str, new_text: &str) -> bool {
    return is_large_count_change(old_text.len(), new_text.len());
}

#[must_use]
fn is_large_newline_count_change(old_text: &str, new_text: &str) -> bool {
    let old_newline_count = bytecount::count(old_text.as_bytes(), b'\n');
    let new_newline_count = bytecount::count(new_text.as_bytes(), b'\n');
    return is_large_count_change(old_newline_count, new_newline_count);
}

#[must_use]
fn to_lines(old: &str, new: &str) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();
    for highlighted_old_line in old.lines() {
        lines.push(highlighted_old_line.to_string());
    }
    if (!old.is_empty()) && !old.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for highlighted_new_line in new.lines() {
        lines.push(highlighted_new_line.to_string());
    }
    if (!new.is_empty()) && !new.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(simple_format(&"".to_string(), &"".to_string()), empty);

        // Test adds-only
        assert_eq!(
            simple_format(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + NEW + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + NEW + "+a" + NORMAL,
                "".to_string() + NEW + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + OLD + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + OLD + "-a" + NORMAL,
                "".to_string() + OLD + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        let result = format(&"<quotes>\n".to_string(), &"[quotes]\n".to_string());
        assert_eq!(
            result,
            [
                format!(
                    "{}-{}<{}quotes{}>{}",
                    OLD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}",
                    NEW, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    #[test]
    fn test_almost_empty_changes() {
        let result = format(&"x\n".to_string(), &"".to_string());
        assert_eq!(result, [format!("{}-x{}", OLD, NORMAL),]);

        let result = format(&"".to_string(), &"x\n".to_string());
        assert_eq!(result, [format!("{}+x{}", NEW, NORMAL),]);
    }

    #[test]
    fn test_is_large_byte_count_change() {
        assert_eq!(is_large_byte_count_change("", ""), false);

        assert_eq!(
            is_large_byte_count_change("", &"x".repeat(SMALL_COUNT_CHANGE)),
            false
        );
        assert_eq!(
            is_large_byte_count_change("", &"x".repeat(SMALL_COUNT_CHANGE + 1)),
            true
        );

        // Verify that doubling the length counts as large
        let base_len = SMALL_COUNT_CHANGE * 2;
        let double_len = base_len * 2;
        let almost_double_len = double_len - 1;
        assert_eq!(
            is_large_byte_count_change(&"x".repeat(base_len), &"y".repeat(almost_double_len)),
            false
        );
        assert_eq!(
            is_large_byte_count_change(&"x".repeat(base_len), &"y".repeat(double_len)),
            true
        );
    }
}
<commit_msg>Implement partial format<commit_after>use std::cmp::{max, min};

use crate::tokenizer;
use crate::{
    constants::*,
    token_collector::{Style, StyledToken, TokenCollector},
};
use diffus::{
    edit::{self, collection},
    Diffable,
};

/// If more than this percentage of either adds or moves is highlighted, we
/// consider it to be a replacement rather than a move, and skip highlighting
/// it.
const MAX_HIGHLIGHT_PERCENTAGE: usize = 30;

const LARGE_COUNT_CHANGE_PERCENT: usize = 100;
const SMALL_COUNT_CHANGE: usize = 10;

/// Like format!(), but faster for our special case
fn format_simple_line(old_new: &str, plus_minus: char, contents: &str) -> String {
    let mut line = String::with_capacity(old_new.len() + 1 + contents.len() + NORMAL.len());
    line.push_str(old_new);
    line.push(plus_minus);
    line.push_str(contents);
    line.push_str(NORMAL);
    return line;
}

/// Format old and new lines in OLD and NEW colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format(old_text: &str, new_text: &str) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for old_line in old_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        lines.push(format_simple_line(OLD, '-', old_line));
    }
    if (!old_text.is_empty()) && !old_text.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in new_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        lines.push(format_simple_line(NEW, '+', add_line));
    }
    if (!new_text.is_empty()) && !new_text.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

/// Returns the last byte index of the nth line of the given string.
fn last_byte_index_of_nth_line(text: &str, line_count: usize) -> usize {
    let mut newlines_found: usize = 0;
    for (byte_index, c) in text.char_indices() {
        if c != '\n' {
            continue;
        }

        // Newline found
        newlines_found += 1;
        if line_count == newlines_found {
            return byte_index;
        }
    }

    panic!("Line {} not found in \n{}", line_count, text);
}

/// If old has 2 lines and new 30, try highlighting changes between old and the
/// first 2 lines of new.
///
/// Test case: testdata/partial-refine.diff
#[must_use]
fn partial_format(old_text: &str, new_text: &str) -> Vec<String> {
    let old_linecount = old_text.lines().count();
    let new_linecount = new_text.lines().count();

    if old_linecount == new_linecount {
        return simple_format(old_text, new_text);
    }

    if old_linecount > new_linecount {
        // FIXME: Do a partial diff in this case as well rather than just giving
        // up and simple_format()ting the text.
        return simple_format(old_text, new_text);
    }

    // Invariant at this point: old_text has fewer lines than new_text

    if !old_text.ends_with('\n') {
        // old_text does *not* end in a newline

        // FIXME: Write tests for and handle this case, needs some thought on
        // how to poplulate new_initial_lines, and how to merge the results at
        // the end of this function.
        return simple_format(old_text, new_text);
    }

    // Extract the old_linecount initial lines from new_text.
    let new_initial_lines_last_offset = last_byte_index_of_nth_line(new_text, old_linecount);
    let new_remaining_lines_first_offset = new_initial_lines_last_offset + 1;
    let new_initial_lines = &new_text[0..new_remaining_lines_first_offset];

    let mut old_text_vs_new_initial_lines = format(old_text, new_initial_lines);

    // Extract the remaining lines from new_text
    let new_remaining_lines = &new_text[new_remaining_lines_first_offset..];
    let mut new_remaining_lines = simple_format("", new_remaining_lines);

    let mut return_me: Vec<String> = Vec::new();
    return_me.append(&mut old_text_vs_new_initial_lines);
    return_me.append(&mut new_remaining_lines);
    return return_me;
}

/// Returns a vector of ANSI highlighted lines
#[must_use]
pub fn format(old_text: &str, new_text: &str) -> Vec<String> {
    if old_text.is_empty() || new_text.is_empty() {
        return simple_format(old_text, new_text);
    }

    // These checks make us faster, please use the benchmark.py script before
    // and after if you change this.
    if is_large_newline_count_change(old_text, new_text) {
        return partial_format(old_text, new_text);
    }
    if is_large_byte_count_change(old_text, new_text) {
        return simple_format(old_text, new_text);
    }

    // Find diffs between adds and removals
    let mut old_collector = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
    let mut new_collector = TokenCollector::create(StyledToken::new("+".to_string(), Style::New));

    // Tokenize adds and removes before diffing them
    let tokenized_old = tokenizer::tokenize(old_text);
    let tokenized_new = tokenizer::tokenize(new_text);

    let diff = tokenized_old.diff(&tokenized_new);
    match diff {
        edit::Edit::Copy(unchanged) => {
            for token in unchanged {
                old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                new_collector.push(StyledToken::new(token.to_string(), Style::New));
            }
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        collection::Edit::Copy(token) => {
                            old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                            new_collector.push(StyledToken::new(token.to_string(), Style::New));
                        }
                        collection::Edit::Insert(token) => {
                            if *token == "\n" {
                                // Make sure the highlighted linefeed is visible
                                new_collector
                                    .push(StyledToken::new("⏎".to_string(), Style::NewInverse));
                            }
                            new_collector
                                .push(StyledToken::new(token.to_string(), Style::NewInverse));
                        }
                        collection::Edit::Remove(token) => {
                            if *token == "\n" {
                                // Make sure the highlighted linefeed is visible
                                old_collector
                                    .push(StyledToken::new("⏎".to_string(), Style::OldInverse));
                            }
                            old_collector
                                .push(StyledToken::new(token.to_string(), Style::OldInverse));
                        }
                        collection::Edit::Change(_) => unimplemented!("Not implemented, help!"),
                    };
                })
                .for_each(drop);
        }
    }

    let highlighted_old_text = old_collector.render();
    let highlighted_new_text = new_collector.render();

    let highlighted_bytes_count =
        old_collector.highlighted_chars_count() + new_collector.highlighted_chars_count();
    let bytes_count = old_collector.chars_count() + new_collector.chars_count();

    // Don't highlight too much
    if (100 * highlighted_bytes_count) / bytes_count > MAX_HIGHLIGHT_PERCENTAGE {
        return partial_format(old_text, new_text);
    }

    return to_lines(&highlighted_old_text, &highlighted_new_text);
}

#[must_use]
fn is_large_count_change(count1: usize, count2: usize) -> bool {
    // This check makes us ignore some changes, thus making us faster. Please
    // use the benchmark.py script before and after if you touch this code.

    let high_count = max(count1, count2);
    let low_count = min(count1, count2);

    if high_count - low_count <= SMALL_COUNT_CHANGE {
        return false;
    }

    // "+ 99" makes the result round up, so 0->0, 1->2.
    let low_count_plus_percentage = (low_count * (LARGE_COUNT_CHANGE_PERCENT + 100) + 99) / 100;

    return high_count >= low_count_plus_percentage;
}

#[must_use]
fn is_large_byte_count_change(old_text: &str, new_text: &str) -> bool {
    return is_large_count_change(old_text.len(), new_text.len());
}

#[must_use]
fn is_large_newline_count_change(old_text: &str, new_text: &str) -> bool {
    let old_newline_count = bytecount::count(old_text.as_bytes(), b'\n');
    let new_newline_count = bytecount::count(new_text.as_bytes(), b'\n');
    return is_large_count_change(old_newline_count, new_newline_count);
}

#[must_use]
fn to_lines(old: &str, new: &str) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();
    for highlighted_old_line in old.lines() {
        lines.push(highlighted_old_line.to_string());
    }
    if (!old.is_empty()) && !old.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for highlighted_new_line in new.lines() {
        lines.push(highlighted_new_line.to_string());
    }
    if (!new.is_empty()) && !new.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(simple_format(&"".to_string(), &"".to_string()), empty);

        // Test adds-only
        assert_eq!(
            simple_format(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + NEW + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + NEW + "+a" + NORMAL,
                "".to_string() + NEW + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + OLD + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + OLD + "-a" + NORMAL,
                "".to_string() + OLD + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        let result = format(&"<quotes>\n".to_string(), &"[quotes]\n".to_string());
        assert_eq!(
            result,
            [
                format!(
                    "{}-{}<{}quotes{}>{}",
                    OLD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}",
                    NEW, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    #[test]
    fn test_almost_empty_changes() {
        let result = format(&"x\n".to_string(), &"".to_string());
        assert_eq!(result, [format!("{}-x{}", OLD, NORMAL),]);

        let result = format(&"".to_string(), &"x\n".to_string());
        assert_eq!(result, [format!("{}+x{}", NEW, NORMAL),]);
    }

    #[test]
    fn test_is_large_byte_count_change() {
        assert_eq!(is_large_byte_count_change("", ""), false);

        assert_eq!(
            is_large_byte_count_change("", &"x".repeat(SMALL_COUNT_CHANGE)),
            false
        );
        assert_eq!(
            is_large_byte_count_change("", &"x".repeat(SMALL_COUNT_CHANGE + 1)),
            true
        );

        // Verify that doubling the length counts as large
        let base_len = SMALL_COUNT_CHANGE * 2;
        let double_len = base_len * 2;
        let almost_double_len = double_len - 1;
        assert_eq!(
            is_large_byte_count_change(&"x".repeat(base_len), &"y".repeat(almost_double_len)),
            false
        );
        assert_eq!(
            is_large_byte_count_change(&"x".repeat(base_len), &"y".repeat(double_len)),
            true
        );
    }
}
<|endoftext|>


<commit_before>use super::*;

pub trait Request {
    type Params;
    type Result;
    const METHOD: &'static str;
}

#[macro_export]
macro_rules! lsp_request {
    ("initialize") => {
        $crate::request::Initialize
    };
    ("shutdown") => {
        $crate::request::Shutdown
    };

    ("window/showMessageRequest") => {
        $crate::request::ShowMessageRequest
    };

    ("client/registerCapability") => {
        $crate::request::RegisterCapability
    };
    ("client/unregisterCapability") => {
        $crate::request::UnregisterCapability
    };

    ("workspace/symbol") => {
        $crate::request::WorkspaceSymbol
    };
    ("workspace/executeCommand") => {
        $crate::request::ExecuteCommand
    };

    ("textDocument/completion") => {
        $crate::request::Completion
    };
    ("completionItem/resolve") => {
        $crate::request::ResolveCompletionItem
    };
    ("textDocument/hover") => {
        $crate::request::HoverRequest
    };
    ("textDocument/signatureHelp") => {
        $crate::request::SignatureHelpRequest
    };
    ("textDocument/definition") => {
        $crate::request::GotoDefinition
    };
    ("textDocument/references") => {
        $crate::request::References
    };
    ("textDocument/documentHighlight") => {
        $crate::request::DocumentHighlightRequest
    };
    ("textDocument/documentSymbol") => {
        $crate::request::DocumentSymbolRequest
    };
    ("textDocument/codeAction") => {
        $crate::request::CodeActionRequest
    };
    ("textDocument/codeLens") => {
        $crate::request::CodeLensRequest
    };
    ("codeLens/resolve") => {
        $crate::request::CodeLensResolve
    };
    ("textDocument/documentLink") => {
        $crate::request::DocumentLinkRequest
    };
    ("documentLink/resolve") => {
        $crate::request::DocumentLinkResolve
    };
    ("workspace/applyEdit") => {
        $crate::request::ApplyWorkspaceEdit
    };
    ("textDocument/rangeFormatting") => {
        $crate::request::RangeFormatting
    };
    ("textDocument/onTypeFormatting") => {
        $crate::request::OnTypeFormatting
    };
    ("textDocument/formatting") => {
        $crate::request::Formatting
    };
    ("textDocument/rename") => {
        $crate::request::Rename
    };
    ("textDocument/documentColor") => {
        $crate::request::DocumentColor
    };
    ("textDocument/colorPresentation") => {
        $crate::request::ColorPresentationRequest
    };
}

/**

 The initialize request is sent as the first request from the client to the server.
 If the server receives request or notification before the `initialize` request it should act as follows:

 * for a request the respond should be errored with `code: -32001`. The message can be picked by the server.
 * notifications should be dropped.

*/
#[derive(Debug)]
pub enum Initialize {}

impl Request for Initialize {
    type Params = InitializeParams;
    type Result = InitializeResult;
    const METHOD: &'static str = "initialize";
}

/**
 * The shutdown request is sent from the client to the server. It asks the server to shut down,
 * but to not exit (otherwise the response might not be delivered correctly to the client).
 * There is a separate exit notification that asks the server to exit.
 */
#[derive(Debug)]
pub enum Shutdown {}

impl Request for Shutdown {
    type Params = ();
    type Result = ();
    const METHOD: &'static str = "shutdown";
}

/**
 * The show message request is sent from a server to a client to ask the client to display a particular message
 * in the user interface. In addition to the show message notification the request allows to pass actions and to
 * wait for an answer from the client.
 */
#[derive(Debug)]
pub enum ShowMessageRequest {}

impl Request for ShowMessageRequest {
    type Params = ShowMessageRequestParams;
    type Result = Option<MessageActionItem>;
    const METHOD: &'static str = "window/showMessageRequest";
}

/**
 * The client/registerCapability request is sent from the server to the client to register for a new capability on the client side. Not all clients need to support dynamic capability registration. A client opts in via the ClientCapabilities.GenericCapability property.
 */
#[derive(Debug)]
pub enum RegisterCapability {}

impl Request for RegisterCapability {
    type Params = RegistrationParams;
    type Result = ();
    const METHOD: &'static str = "client/registerCapability";
}

/// The client/unregisterCapability request is sent from the server to the client to unregister a
/// previously register capability.
#[derive(Debug)]
pub enum UnregisterCapability {}

impl Request for UnregisterCapability {
    type Params = UnregistrationParams;
    type Result = ();
    const METHOD: &'static str = "client/unregisterCapability";
}

/**
 The Completion request is sent from the client to the server to compute completion items at a given cursor position.
 Completion items are presented in the IntelliSense user interface. If computing full completion items is expensive,
 servers can additionally provide a handler for the completion item resolve request ('completionItem/resolve').
 This request is sent when a completion item is selected in the user interface. A typically use case is for example:
 the 'textDocument/completion' request doesn't fill in the documentation property for returned completion items
 since it is expensive to compute. When the item is selected in the user interface then a 'completionItem/resolve'
 request is sent with the selected completion item as a param. The returned completion item should have the
 documentation property filled in.
*/
#[derive(Debug)]
pub enum Completion {}

impl Request for Completion {
    type Params = CompletionParams;
    type Result = Option<CompletionResponse>;
    const METHOD: &'static str = "textDocument/completion";
}

/// The request is sent from the client to the server to resolve additional information for a given completion item.
#[derive(Debug)]
pub enum ResolveCompletionItem {}

impl Request for ResolveCompletionItem {
    type Params = CompletionItem;
    type Result = CompletionItem;
    const METHOD: &'static str = "completionItem/resolve";
}

/// The hover request is sent from the client to the server to request hover information at a given text
/// document position.
#[derive(Debug)]
pub enum HoverRequest {}

impl Request for HoverRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<Hover>;
    const METHOD: &'static str = "textDocument/hover";
}

/// The signature help request is sent from the client to the server to request signature information at
/// a given cursor position.
#[derive(Debug)]
pub enum SignatureHelpRequest {}

impl Request for SignatureHelpRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<SignatureHelp>;
    const METHOD: &'static str = "textDocument/signatureHelp";
}

/// The goto definition request is sent from the client to the server to resolve the definition location of
/// a symbol at a given text document position.
#[derive(Debug)]
pub enum GotoDefinition {}

impl Request for GotoDefinition {
    type Params = TextDocumentPositionParams;
    type Result = Option<GotoDefinitionResponse>;
    const METHOD: &'static str = "textDocument/definition";
}

/**
 * GotoDefinition response can be single location or multiple ones.
 */
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GotoDefinitionResponse {
    Scalar(Location),
    Array(Vec<Location>),
}

/// The references request is sent from the client to the server to resolve project-wide references for the
/// symbol denoted by the given text document position.
#[derive(Debug)]
pub enum References {}

impl Request for References {
    type Params = ReferenceParams;
    type Result = Option<Vec<Location>>;
    const METHOD: &'static str = "textDocument/references";
}

/// The goto type definition request is sent from the client to the
/// server to resolve the type definition location of a symbol at a
/// given text document position.
#[derive(Debug)]
pub enum GotoTypeDefinition {}

pub type GotoTypeDefinitionResponse = GotoDefinitionResponse;

impl Request for GotoTypeDefinition {
    type Params = TextDocumentPositionParams;
    type Result = Option<GotoTypeDefinitionResponse>;
    const METHOD: &'static str = "textDocument/typeDefinition";
}

/// The goto implementation request is sent from the client to the
/// server to resolve the implementation location of a symbol at a
/// given text document position.
#[derive(Debug)]
pub enum GotoImplementation {}

pub type GotoImplementationResponse = GotoDefinitionResponse;

impl Request for GotoImplementation {
    type Params = TextDocumentPositionParams;
    type Result = Option<GotoImplementationResponse>;
    const METHOD: &'static str = "textDocument/implementation";
}

/**
 The document highlight request is sent from the client to the server to resolve a document highlights
 for a given text document position.
 For programming languages this usually highlights all references to the symbol scoped to this file.
 However we kept 'textDocument/documentHighlight' and 'textDocument/references' separate requests since
 the first one is allowed to be more fuzzy.
 Symbol matches usually have a DocumentHighlightKind of Read or Write whereas fuzzy or textual matches
 use Textas the kind.
*/
#[derive(Debug)]
pub enum DocumentHighlightRequest {}

impl Request for DocumentHighlightRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<Vec<DocumentHighlight>>;
    const METHOD: &'static str = "textDocument/documentHighlight";
}

/**
 * The document symbol request is sent from the client to the server to list all symbols found in a given
 * text document.
 */
#[derive(Debug)]
pub enum DocumentSymbolRequest {}

impl Request for DocumentSymbolRequest {
    type Params = DocumentSymbolParams;
    type Result = Option<DocumentSymbolResponse>;
    const METHOD: &'static str = "textDocument/documentSymbol";
}

/**
 * The workspace symbol request is sent from the client to the server to list project-wide symbols
 * matching the query string.
 */
#[derive(Debug)]
pub enum WorkspaceSymbol {}

impl Request for WorkspaceSymbol {
    type Params = WorkspaceSymbolParams;
    type Result = Option<Vec<SymbolInformation>>;
    const METHOD: &'static str = "workspace/symbol";
}

/// The workspace/executeCommand request is sent from the client to the server to trigger command execution on the server. In most cases the server creates a WorkspaceEdit structure and applies the changes to the workspace using the request workspace/applyEdit which is sent from the server to the client.
#[derive(Debug)]
pub enum ExecuteCommand {}

impl Request for ExecuteCommand {
    type Params = ExecuteCommandParams;
    type Result = Option<Value>;
    const METHOD: &'static str = "workspace/executeCommand";
}

/// The workspace/applyEdit request is sent from the server to the client to modify resource on the
/// client side.
#[derive(Debug)]
pub enum ApplyWorkspaceEdit {}

impl Request for ApplyWorkspaceEdit {
    type Params = ApplyWorkspaceEditParams;
    type Result = ApplyWorkspaceEditResponse;
    const METHOD: &'static str = "workspace/applyEdit";
}

/**
 * The code action request is sent from the client to the server to compute commands for a given text document
 * and range. The request is triggered when the user moves the cursor into a problem marker in the editor or
 * presses the lightbulb associated with a marker.
 */
#[derive(Debug)]
pub enum CodeActionRequest {}

impl Request for CodeActionRequest {
    type Params = CodeActionParams;
    type Result = Option<Vec<Command>>;
    const METHOD: &'static str = "textDocument/codeAction";
}

/**
 * The code lens request is sent from the client to the server to compute code lenses for a given text document.
 */
#[derive(Debug)]
pub enum CodeLensRequest {}

impl Request for CodeLensRequest {
    type Params = CodeLensParams;
    type Result = Option<Vec<CodeLens>>;
    const METHOD: &'static str = "textDocument/codeLens";
}

/**
 * The code lens resolve request is sent from the client to the server to resolve the command for a
 * given code lens item.
 */
#[derive(Debug)]
pub enum CodeLensResolve {}

impl Request for CodeLensResolve {
    type Params = CodeLens;
    type Result = CodeLens;
    const METHOD: &'static str = "codeLens/resolve";
}

/// The document links request is sent from the client to the server to request the location of links in a document.
#[derive(Debug)]
pub enum DocumentLinkRequest {}

impl Request for DocumentLinkRequest {
    type Params = DocumentLinkParams;
    type Result = Option<Vec<DocumentLink>>;
    const METHOD: &'static str = "textDocument/documentLink";
}

/**

 The document link resolve request is sent from the client to the server to resolve the target of
 a given document link.

*/
#[derive(Debug)]
pub enum DocumentLinkResolve {}

impl Request for DocumentLinkResolve {
    type Params = DocumentLink;
    type Result = DocumentLink;
    const METHOD: &'static str = "documentLink/resolve";
}

/**
 * The document formatting request is sent from the server to the client to format a whole document.
 */
#[derive(Debug)]
pub enum Formatting {}

impl Request for Formatting {
    type Params = DocumentFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/formatting";
}

/// The document range formatting request is sent from the client to the server to format a given range in a document.
#[derive(Debug)]
pub enum RangeFormatting {}

impl Request for RangeFormatting {
    type Params = DocumentRangeFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/rangeFormatting";
}

/**
 * The document on type formatting request is sent from the client to the server to format parts of
 * the document during typing.
 */
#[derive(Debug)]
pub enum OnTypeFormatting {}

impl Request for OnTypeFormatting {
    type Params = DocumentOnTypeFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/onTypeFormatting";
}

/**
 * The rename request is sent from the client to the server to perform a workspace-wide rename of a symbol.
 */
#[derive(Debug)]
pub enum Rename {}

impl Request for Rename {
    type Params = RenameParams;
    type Result = Option<WorkspaceEdit>;
    const METHOD: &'static str = "textDocument/rename";
}

#[derive(Debug)]
pub enum DocumentColor {}

impl Request for DocumentColor {
    type Params = DocumentColorParams;
    type Result = Option<Vec<ColorInformation>>;
    const METHOD: &'static str = "textDocument/documentColor";
}

#[derive(Debug)]
pub enum ColorPresentationRequest {}

impl Request for ColorPresentationRequest {
    type Params = ColorPresentationParams;
    type Result = Option<Vec<ColorPresentation>>;
    const METHOD: &'static str = "textDocument/colorPresentation";
}

#[cfg(test)]
mod test {
    use super::*;

    fn fake_call<R>()
        where R: Request,
              R::Params: serde::Serialize,
              R::Result: serde::de::DeserializeOwned
    {
    }

    macro_rules! check_macro {
        ($name:tt) => {
            // check whethe the macro name matches the method
            assert_eq!(<lsp_request!($name) as Request>::METHOD, $name);
            // test whether type checking passes for each component
            fake_call::<lsp_request!($name)>();
        }
    }

    #[test]
    fn check_macro_definitions() {
        check_macro!("initialize");
        check_macro!("shutdown");
        check_macro!("window/showMessageRequest");
        check_macro!("client/registerCapability");
        check_macro!("client/unregisterCapability");
        check_macro!("workspace/symbol");
        check_macro!("workspace/executeCommand");
        check_macro!("textDocument/completion");
        check_macro!("completionItem/resolve");
        check_macro!("textDocument/hover");
        check_macro!("textDocument/signatureHelp");
        check_macro!("textDocument/definition");
        check_macro!("textDocument/references");
        check_macro!("textDocument/documentHighlight");
        check_macro!("textDocument/documentSymbol");
        check_macro!("textDocument/codeAction");
        check_macro!("textDocument/codeLens");
        check_macro!("codeLens/resolve");
        check_macro!("textDocument/documentLink");
        check_macro!("documentLink/resolve");
        check_macro!("workspace/applyEdit");
        check_macro!("textDocument/rangeFormatting");
        check_macro!("textDocument/onTypeFormatting");
        check_macro!("textDocument/formatting");
        check_macro!("textDocument/rename");
        check_macro!("textDocument/documentColor");
        check_macro!("textDocument/colorPresentation");
    }
}
<commit_msg>Remove option per spec<commit_after>use super::*;

pub trait Request {
    type Params;
    type Result;
    const METHOD: &'static str;
}

#[macro_export]
macro_rules! lsp_request {
    ("initialize") => {
        $crate::request::Initialize
    };
    ("shutdown") => {
        $crate::request::Shutdown
    };

    ("window/showMessageRequest") => {
        $crate::request::ShowMessageRequest
    };

    ("client/registerCapability") => {
        $crate::request::RegisterCapability
    };
    ("client/unregisterCapability") => {
        $crate::request::UnregisterCapability
    };

    ("workspace/symbol") => {
        $crate::request::WorkspaceSymbol
    };
    ("workspace/executeCommand") => {
        $crate::request::ExecuteCommand
    };

    ("textDocument/completion") => {
        $crate::request::Completion
    };
    ("completionItem/resolve") => {
        $crate::request::ResolveCompletionItem
    };
    ("textDocument/hover") => {
        $crate::request::HoverRequest
    };
    ("textDocument/signatureHelp") => {
        $crate::request::SignatureHelpRequest
    };
    ("textDocument/definition") => {
        $crate::request::GotoDefinition
    };
    ("textDocument/references") => {
        $crate::request::References
    };
    ("textDocument/documentHighlight") => {
        $crate::request::DocumentHighlightRequest
    };
    ("textDocument/documentSymbol") => {
        $crate::request::DocumentSymbolRequest
    };
    ("textDocument/codeAction") => {
        $crate::request::CodeActionRequest
    };
    ("textDocument/codeLens") => {
        $crate::request::CodeLensRequest
    };
    ("codeLens/resolve") => {
        $crate::request::CodeLensResolve
    };
    ("textDocument/documentLink") => {
        $crate::request::DocumentLinkRequest
    };
    ("documentLink/resolve") => {
        $crate::request::DocumentLinkResolve
    };
    ("workspace/applyEdit") => {
        $crate::request::ApplyWorkspaceEdit
    };
    ("textDocument/rangeFormatting") => {
        $crate::request::RangeFormatting
    };
    ("textDocument/onTypeFormatting") => {
        $crate::request::OnTypeFormatting
    };
    ("textDocument/formatting") => {
        $crate::request::Formatting
    };
    ("textDocument/rename") => {
        $crate::request::Rename
    };
    ("textDocument/documentColor") => {
        $crate::request::DocumentColor
    };
    ("textDocument/colorPresentation") => {
        $crate::request::ColorPresentationRequest
    };
}

/**

 The initialize request is sent as the first request from the client to the server.
 If the server receives request or notification before the `initialize` request it should act as follows:

 * for a request the respond should be errored with `code: -32001`. The message can be picked by the server.
 * notifications should be dropped.

*/
#[derive(Debug)]
pub enum Initialize {}

impl Request for Initialize {
    type Params = InitializeParams;
    type Result = InitializeResult;
    const METHOD: &'static str = "initialize";
}

/**
 * The shutdown request is sent from the client to the server. It asks the server to shut down,
 * but to not exit (otherwise the response might not be delivered correctly to the client).
 * There is a separate exit notification that asks the server to exit.
 */
#[derive(Debug)]
pub enum Shutdown {}

impl Request for Shutdown {
    type Params = ();
    type Result = ();
    const METHOD: &'static str = "shutdown";
}

/**
 * The show message request is sent from a server to a client to ask the client to display a particular message
 * in the user interface. In addition to the show message notification the request allows to pass actions and to
 * wait for an answer from the client.
 */
#[derive(Debug)]
pub enum ShowMessageRequest {}

impl Request for ShowMessageRequest {
    type Params = ShowMessageRequestParams;
    type Result = Option<MessageActionItem>;
    const METHOD: &'static str = "window/showMessageRequest";
}

/**
 * The client/registerCapability request is sent from the server to the client to register for a new capability on the client side. Not all clients need to support dynamic capability registration. A client opts in via the ClientCapabilities.GenericCapability property.
 */
#[derive(Debug)]
pub enum RegisterCapability {}

impl Request for RegisterCapability {
    type Params = RegistrationParams;
    type Result = ();
    const METHOD: &'static str = "client/registerCapability";
}

/// The client/unregisterCapability request is sent from the server to the client to unregister a
/// previously register capability.
#[derive(Debug)]
pub enum UnregisterCapability {}

impl Request for UnregisterCapability {
    type Params = UnregistrationParams;
    type Result = ();
    const METHOD: &'static str = "client/unregisterCapability";
}

/**
 The Completion request is sent from the client to the server to compute completion items at a given cursor position.
 Completion items are presented in the IntelliSense user interface. If computing full completion items is expensive,
 servers can additionally provide a handler for the completion item resolve request ('completionItem/resolve').
 This request is sent when a completion item is selected in the user interface. A typically use case is for example:
 the 'textDocument/completion' request doesn't fill in the documentation property for returned completion items
 since it is expensive to compute. When the item is selected in the user interface then a 'completionItem/resolve'
 request is sent with the selected completion item as a param. The returned completion item should have the
 documentation property filled in.
*/
#[derive(Debug)]
pub enum Completion {}

impl Request for Completion {
    type Params = CompletionParams;
    type Result = Option<CompletionResponse>;
    const METHOD: &'static str = "textDocument/completion";
}

/// The request is sent from the client to the server to resolve additional information for a given completion item.
#[derive(Debug)]
pub enum ResolveCompletionItem {}

impl Request for ResolveCompletionItem {
    type Params = CompletionItem;
    type Result = CompletionItem;
    const METHOD: &'static str = "completionItem/resolve";
}

/// The hover request is sent from the client to the server to request hover information at a given text
/// document position.
#[derive(Debug)]
pub enum HoverRequest {}

impl Request for HoverRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<Hover>;
    const METHOD: &'static str = "textDocument/hover";
}

/// The signature help request is sent from the client to the server to request signature information at
/// a given cursor position.
#[derive(Debug)]
pub enum SignatureHelpRequest {}

impl Request for SignatureHelpRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<SignatureHelp>;
    const METHOD: &'static str = "textDocument/signatureHelp";
}

/// The goto definition request is sent from the client to the server to resolve the definition location of
/// a symbol at a given text document position.
#[derive(Debug)]
pub enum GotoDefinition {}

impl Request for GotoDefinition {
    type Params = TextDocumentPositionParams;
    type Result = Option<GotoDefinitionResponse>;
    const METHOD: &'static str = "textDocument/definition";
}

/**
 * GotoDefinition response can be single location or multiple ones.
 */
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GotoDefinitionResponse {
    Scalar(Location),
    Array(Vec<Location>),
}

/// The references request is sent from the client to the server to resolve project-wide references for the
/// symbol denoted by the given text document position.
#[derive(Debug)]
pub enum References {}

impl Request for References {
    type Params = ReferenceParams;
    type Result = Option<Vec<Location>>;
    const METHOD: &'static str = "textDocument/references";
}

/// The goto type definition request is sent from the client to the
/// server to resolve the type definition location of a symbol at a
/// given text document position.
#[derive(Debug)]
pub enum GotoTypeDefinition {}

pub type GotoTypeDefinitionResponse = GotoDefinitionResponse;

impl Request for GotoTypeDefinition {
    type Params = TextDocumentPositionParams;
    type Result = Option<GotoTypeDefinitionResponse>;
    const METHOD: &'static str = "textDocument/typeDefinition";
}

/// The goto implementation request is sent from the client to the
/// server to resolve the implementation location of a symbol at a
/// given text document position.
#[derive(Debug)]
pub enum GotoImplementation {}

pub type GotoImplementationResponse = GotoDefinitionResponse;

impl Request for GotoImplementation {
    type Params = TextDocumentPositionParams;
    type Result = Option<GotoImplementationResponse>;
    const METHOD: &'static str = "textDocument/implementation";
}

/**
 The document highlight request is sent from the client to the server to resolve a document highlights
 for a given text document position.
 For programming languages this usually highlights all references to the symbol scoped to this file.
 However we kept 'textDocument/documentHighlight' and 'textDocument/references' separate requests since
 the first one is allowed to be more fuzzy.
 Symbol matches usually have a DocumentHighlightKind of Read or Write whereas fuzzy or textual matches
 use Textas the kind.
*/
#[derive(Debug)]
pub enum DocumentHighlightRequest {}

impl Request for DocumentHighlightRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<Vec<DocumentHighlight>>;
    const METHOD: &'static str = "textDocument/documentHighlight";
}

/**
 * The document symbol request is sent from the client to the server to list all symbols found in a given
 * text document.
 */
#[derive(Debug)]
pub enum DocumentSymbolRequest {}

impl Request for DocumentSymbolRequest {
    type Params = DocumentSymbolParams;
    type Result = Option<DocumentSymbolResponse>;
    const METHOD: &'static str = "textDocument/documentSymbol";
}

/**
 * The workspace symbol request is sent from the client to the server to list project-wide symbols
 * matching the query string.
 */
#[derive(Debug)]
pub enum WorkspaceSymbol {}

impl Request for WorkspaceSymbol {
    type Params = WorkspaceSymbolParams;
    type Result = Option<Vec<SymbolInformation>>;
    const METHOD: &'static str = "workspace/symbol";
}

/// The workspace/executeCommand request is sent from the client to the server to trigger command execution on the server. In most cases the server creates a WorkspaceEdit structure and applies the changes to the workspace using the request workspace/applyEdit which is sent from the server to the client.
#[derive(Debug)]
pub enum ExecuteCommand {}

impl Request for ExecuteCommand {
    type Params = ExecuteCommandParams;
    type Result = Option<Value>;
    const METHOD: &'static str = "workspace/executeCommand";
}

/// The workspace/applyEdit request is sent from the server to the client to modify resource on the
/// client side.
#[derive(Debug)]
pub enum ApplyWorkspaceEdit {}

impl Request for ApplyWorkspaceEdit {
    type Params = ApplyWorkspaceEditParams;
    type Result = ApplyWorkspaceEditResponse;
    const METHOD: &'static str = "workspace/applyEdit";
}

/**
 * The code action request is sent from the client to the server to compute commands for a given text document
 * and range. The request is triggered when the user moves the cursor into a problem marker in the editor or
 * presses the lightbulb associated with a marker.
 */
#[derive(Debug)]
pub enum CodeActionRequest {}

impl Request for CodeActionRequest {
    type Params = CodeActionParams;
    type Result = Option<Vec<Command>>;
    const METHOD: &'static str = "textDocument/codeAction";
}

/**
 * The code lens request is sent from the client to the server to compute code lenses for a given text document.
 */
#[derive(Debug)]
pub enum CodeLensRequest {}

impl Request for CodeLensRequest {
    type Params = CodeLensParams;
    type Result = Option<Vec<CodeLens>>;
    const METHOD: &'static str = "textDocument/codeLens";
}

/**
 * The code lens resolve request is sent from the client to the server to resolve the command for a
 * given code lens item.
 */
#[derive(Debug)]
pub enum CodeLensResolve {}

impl Request for CodeLensResolve {
    type Params = CodeLens;
    type Result = CodeLens;
    const METHOD: &'static str = "codeLens/resolve";
}

/// The document links request is sent from the client to the server to request the location of links in a document.
#[derive(Debug)]
pub enum DocumentLinkRequest {}

impl Request for DocumentLinkRequest {
    type Params = DocumentLinkParams;
    type Result = Option<Vec<DocumentLink>>;
    const METHOD: &'static str = "textDocument/documentLink";
}

/**

 The document link resolve request is sent from the client to the server to resolve the target of
 a given document link.

*/
#[derive(Debug)]
pub enum DocumentLinkResolve {}

impl Request for DocumentLinkResolve {
    type Params = DocumentLink;
    type Result = DocumentLink;
    const METHOD: &'static str = "documentLink/resolve";
}

/**
 * The document formatting request is sent from the server to the client to format a whole document.
 */
#[derive(Debug)]
pub enum Formatting {}

impl Request for Formatting {
    type Params = DocumentFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/formatting";
}

/// The document range formatting request is sent from the client to the server to format a given range in a document.
#[derive(Debug)]
pub enum RangeFormatting {}

impl Request for RangeFormatting {
    type Params = DocumentRangeFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/rangeFormatting";
}

/**
 * The document on type formatting request is sent from the client to the server to format parts of
 * the document during typing.
 */
#[derive(Debug)]
pub enum OnTypeFormatting {}

impl Request for OnTypeFormatting {
    type Params = DocumentOnTypeFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/onTypeFormatting";
}

/**
 * The rename request is sent from the client to the server to perform a workspace-wide rename of a symbol.
 */
#[derive(Debug)]
pub enum Rename {}

impl Request for Rename {
    type Params = RenameParams;
    type Result = Option<WorkspaceEdit>;
    const METHOD: &'static str = "textDocument/rename";
}

#[derive(Debug)]
pub enum DocumentColor {}

impl Request for DocumentColor {
    type Params = DocumentColorParams;
    type Result = Vec<ColorInformation>;
    const METHOD: &'static str = "textDocument/documentColor";
}

#[derive(Debug)]
pub enum ColorPresentationRequest {}

impl Request for ColorPresentationRequest {
    type Params = ColorPresentationParams;
    type Result = Vec<ColorPresentation>;
    const METHOD: &'static str = "textDocument/colorPresentation";
}

#[cfg(test)]
mod test {
    use super::*;

    fn fake_call<R>()
        where R: Request,
              R::Params: serde::Serialize,
              R::Result: serde::de::DeserializeOwned
    {
    }

    macro_rules! check_macro {
        ($name:tt) => {
            // check whethe the macro name matches the method
            assert_eq!(<lsp_request!($name) as Request>::METHOD, $name);
            // test whether type checking passes for each component
            fake_call::<lsp_request!($name)>();
        }
    }

    #[test]
    fn check_macro_definitions() {
        check_macro!("initialize");
        check_macro!("shutdown");
        check_macro!("window/showMessageRequest");
        check_macro!("client/registerCapability");
        check_macro!("client/unregisterCapability");
        check_macro!("workspace/symbol");
        check_macro!("workspace/executeCommand");
        check_macro!("textDocument/completion");
        check_macro!("completionItem/resolve");
        check_macro!("textDocument/hover");
        check_macro!("textDocument/signatureHelp");
        check_macro!("textDocument/definition");
        check_macro!("textDocument/references");
        check_macro!("textDocument/documentHighlight");
        check_macro!("textDocument/documentSymbol");
        check_macro!("textDocument/codeAction");
        check_macro!("textDocument/codeLens");
        check_macro!("codeLens/resolve");
        check_macro!("textDocument/documentLink");
        check_macro!("documentLink/resolve");
        check_macro!("workspace/applyEdit");
        check_macro!("textDocument/rangeFormatting");
        check_macro!("textDocument/onTypeFormatting");
        check_macro!("textDocument/formatting");
        check_macro!("textDocument/rename");
        check_macro!("textDocument/documentColor");
        check_macro!("textDocument/colorPresentation");
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use config::Route;

/// Map host port to a route of arbitrary type
///
/// Returns destination route and relative path
pub fn route<'x, D>(host: &str, path: &'x str, table: &'x BTreeMap<Route, D>)
    -> Option<(&'x D, &'x str, &'x str)>
{
    // TODO(tailhook) transform into range iteration when `btree_range` is
    // stable
    for (route, result) in table.iter().rev() {
        if route.host == host && path_match(&route.path, path) {
            // Longest match is the last in reversed iteration
            let prefix = route.path.as_ref().map(|x| &x[..]).unwrap_or("");
            return Some((result, prefix, &path[prefix.len()..]));
        }
    }
    return None;
}

fn path_match<S: AsRef<str>>(pattern: &Option<S>, value: &str) -> bool {
    if let Some(ref prefix) = *pattern {
        let prefix = prefix.as_ref();
        if value.starts_with(prefix) && (
                value.len() == prefix.len() ||
                value[prefix.len()..].starts_with("/"))
        {
            return true;
        }
        return false;
    } else {
        return true;
    }
}

/// Returns host with trimmed whitespace and without port number if exists
pub fn parse_host(host_header: &str) -> &str {
    match host_header.find(':') {
        Some(idx) => &host_header[..idx],
        None => host_header,
    }.trim()
}

#[cfg(test)]
mod test {
    use config::Route;
    use super::route;

    #[test]
    fn route_host() {
        let table = vec![
            (Route { host: "example.com".into(), path: None }, 1),
            ].into_iter().collect();
        assert_eq!(route("example.com", "/hello", &table),
                   Some((&1, "", "/hello")));
        assert_eq!(route("example.com", "/", &table),
                   Some((&1, "", "/")));
        assert_eq!(route("example.org", "/hello", &table), None);
        assert_eq!(route("example.org", "/", &table), None);
    }
    /*

    #[test]
    fn route_path() {
        let table = vec![
            (Route { host: "ex.com".into(), path: Some("/one".into()) }, 1),
            (Route { host: "ex.com".into(), path: None }, 0),
            (Route { host: "ex.com".into(), path: Some("/two".into()) }, 2),
            ].into_iter().collect();
        assert_eq!(route("ex.com", "/one", &table),
                   Some((&1, "")));
        assert_eq!(route("ex.com", "/one/end", &table),
                   Some((&1, "/end")));
        assert_eq!(route("ex.com", "/two", &table),
                   Some((&2, "")));
        assert_eq!(route("ex.com","/two/some", &table),
                   Some((&2, "/some")));
        assert_eq!(route("ex.com", "/three", &table),
                   Some((&0, "/three")));
        assert_eq!(route("ex.com", "/", &table),
                   Some((&0, "/")));
        assert_eq!(route("ex.org", "/one", &table), None);
        assert_eq!(route("subdomain.ex.org", "/two", &table), None);
        assert_eq!(route("example.org", "/", &table), None);
        assert_eq!(route("example.org", "/two", &table), None);
    }
    */

}
<commit_msg>fix routing prefix matching to allow query<commit_after>use std::collections::BTreeMap;

use config::Route;

/// Map host port to a route of arbitrary type
///
/// Returns destination route and relative path
pub fn route<'x, D>(host: &str, path: &'x str, table: &'x BTreeMap<Route, D>)
    -> Option<(&'x D, &'x str, &'x str)>
{
    // TODO(tailhook) transform into range iteration when `btree_range` is
    // stable
    for (route, result) in table.iter().rev() {
        if route.host == host && path_match(&route.path, path) {
            // Longest match is the last in reversed iteration
            let prefix = route.path.as_ref().map(|x| &x[..]).unwrap_or("");
            return Some((result, prefix, &path[prefix.len()..]));
        }
    }
    return None;
}

fn path_match<S: AsRef<str>>(pattern: &Option<S>, value: &str) -> bool {
    if let Some(ref prefix) = *pattern {
        let prefix = prefix.as_ref();
        if value.starts_with(prefix) && (
                value.len() == prefix.len() ||
                value[prefix.len()..].starts_with("/") ||
                value[prefix.len()..].starts_with("?"))
        {
            return true;
        }
        return false;
    } else {
        return true;
    }
}

/// Returns host with trimmed whitespace and without port number if exists
pub fn parse_host(host_header: &str) -> &str {
    match host_header.find(':') {
        Some(idx) => &host_header[..idx],
        None => host_header,
    }.trim()
}

#[cfg(test)]
mod test {
    use config::Route;
    use super::route;

    #[test]
    fn route_host() {
        let table = vec![
            (Route { host: "example.com".into(), path: None }, 1),
            ].into_iter().collect();
        assert_eq!(route("example.com", "/hello", &table),
                   Some((&1, "", "/hello")));
        assert_eq!(route("example.com", "/", &table),
                   Some((&1, "", "/")));
        assert_eq!(route("example.org", "/hello", &table), None);
        assert_eq!(route("example.org", "/", &table), None);
    }
    /*

    #[test]
    fn route_path() {
        let table = vec![
            (Route { host: "ex.com".into(), path: Some("/one".into()) }, 1),
            (Route { host: "ex.com".into(), path: None }, 0),
            (Route { host: "ex.com".into(), path: Some("/two".into()) }, 2),
            ].into_iter().collect();
        assert_eq!(route("ex.com", "/one", &table),
                   Some((&1, "")));
        assert_eq!(route("ex.com", "/one/end", &table),
                   Some((&1, "/end")));
        assert_eq!(route("ex.com", "/two", &table),
                   Some((&2, "")));
        assert_eq!(route("ex.com","/two/some", &table),
                   Some((&2, "/some")));
        assert_eq!(route("ex.com", "/three", &table),
                   Some((&0, "/three")));
        assert_eq!(route("ex.com", "/", &table),
                   Some((&0, "/")));
        assert_eq!(route("ex.org", "/one", &table), None);
        assert_eq!(route("subdomain.ex.org", "/two", &table), None);
        assert_eq!(route("example.org", "/", &table), None);
        assert_eq!(route("example.org", "/two", &table), None);
    }
    */

}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]

use std::str::from_utf8;
use std::collections::HashMap;
use std::thread;

use rustc_serialize::json;
use curl::http;

pub const SESSION_TTL: &'static str = "10s";

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionCreate {
    Name: String,
    TTL: String
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionID {
    ID: String
}

pub struct Session {
    endpoint: String
}

impl Session {
    pub fn new(address: &str) ->  Session {
        Session {
            endpoint: format!("http://{}/v1/session", address)
        }
    }

    pub fn create(&self, name: String) -> String{
        let url = format!("{}/create", self.endpoint);
        let session = SessionCreate {
            Name: name,
            TTL: self::SESSION_TTL.to_owned()
        };
        let json_str = json::encode(&session).unwrap();

        let resp = http::handle()
            .put(url, &json_str)
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Consul: Error creating a session!");
        }
        let result = from_utf8(resp.get_body()).unwrap();
        let json_data = match json::Json::from_str(result) {
            Ok(value) => value,
            Err(err) => panic!("consul: Could not convert to json: {:?}", result)
        };
        super::get_string(&json_data, &["ID"])
    }

    pub fn renew(&self, session_id: &String) {
        for i in 0..10 {
            let url = format!("{}/renew/{}", self.endpoint, session_id);
            let resp = http::handle()
                .put(url, "")
                .content_type("application/json")
                .exec().unwrap();
            if resp.get_code() != 200 {
                println!("Could not renew ession: {}, returned HTTP code: {:?}. Sleeping for 1 sec", session_id, resp.get_code());
                if i == 10 {
                    panic!("Cound not renew session: {} after 10 tries. Panicing.", session_id);
                }
                thread::sleep_ms(1000);
            }
            else {
                break;
            }
        }

    }

    pub fn end(&self, session_id: &String) {
        let url = format!("{}/destroy/{}", self.endpoint, session_id);
        let resp = http::handle()
            .put(url, "")
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Cound not destroy session: {}", session_id);
        }

    }

}
<commit_msg>Making session longer<commit_after>#![allow(non_snake_case)]

use std::str::from_utf8;
use std::collections::HashMap;
use std::thread;

use rustc_serialize::json;
use curl::http;

pub const SESSION_TTL: &'static str = "15s";

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionCreate {
    Name: String,
    TTL: String
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionID {
    ID: String
}

pub struct Session {
    endpoint: String
}

impl Session {
    pub fn new(address: &str) ->  Session {
        Session {
            endpoint: format!("http://{}/v1/session", address)
        }
    }

    pub fn create(&self, name: String) -> String{
        let url = format!("{}/create", self.endpoint);
        let session = SessionCreate {
            Name: name,
            TTL: self::SESSION_TTL.to_owned()
        };
        let json_str = json::encode(&session).unwrap();

        let resp = http::handle()
            .put(url, &json_str)
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Consul: Error creating a session!");
        }
        let result = from_utf8(resp.get_body()).unwrap();
        let json_data = match json::Json::from_str(result) {
            Ok(value) => value,
            Err(err) => panic!("consul: Could not convert to json: {:?}", result)
        };
        super::get_string(&json_data, &["ID"])
    }

    pub fn renew(&self, session_id: &String) {
        for i in 0..10 {
            let url = format!("{}/renew/{}", self.endpoint, session_id);
            let resp = http::handle()
                .put(url, "")
                .content_type("application/json")
                .exec().unwrap();
            if resp.get_code() != 200 {
                println!("Could not renew ession: {}, returned HTTP code: {:?}. Sleeping for 1 sec", session_id, resp.get_code());
                if i == 10 {
                    panic!("Cound not renew session: {} after 10 tries. Panicing.", session_id);
                }
                thread::sleep_ms(1000);
            }
            else {
                break;
            }
        }

    }

    pub fn end(&self, session_id: &String) {
        let url = format!("{}/destroy/{}", self.endpoint, session_id);
        let resp = http::handle()
            .put(url, "")
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Cound not destroy session: {}", session_id);
        }

    }

}
<|endoftext|>


<commit_before>use crate::{
    ast::Ast::{self, *},
    grammar::{
        FormPat::{self, *},
        SynEnv,
    },
    name::*,
    util::mbe::EnvMBE,
};

fn node_names_mentioned(pat: &FormPat) -> Vec<Name> {
    match *pat {
        Named(n, ref body) => {
            let mut res = node_names_mentioned(&*body);
            res.push(n);
            res
        }
        Scope(_, _) => vec![],
        Pick(_, _) => vec![],
        Star(ref body)
        | Plus(ref body)
        | NameImport(ref body, _)
        | NameImportPhaseless(ref body, _)
        | VarRef(ref body)
        | Literal(ref body, _)
        | QuoteDeepen(ref body, _)
        | QuoteEscape(ref body, _)
        | Common(ref body)
        | Reserved(ref body, _) => node_names_mentioned(&*body),
        Seq(ref sub_pats) | Alt(ref sub_pats) => {
            let mut res = vec![];
            for pat in sub_pats {
                res.append(&mut node_names_mentioned(pat));
            }
            res
        }
        Biased(ref lhs, ref rhs) => {
            let mut res = node_names_mentioned(&*lhs);
            res.append(&mut node_names_mentioned(&*rhs));
            res
        }
        Anyways(_) | Impossible | Scan(_) | Call(_) | SynImport(_, _, _) => vec![],
    }
}

pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE<Ast>, s: &SynEnv) -> String {
    // HACK: handle underdetermined forms
    let undet = crate::ty_compare::underdetermined_form.with(|u| u.clone());
    match *actl {
        Node(ref form, ref body, _) if form == &undet => {
            return crate::ty_compare::unification.with(|unif| {
                let var = crate::core_forms::ast_to_name(body.get_leaf_or_panic(&n("id")));
                let looked_up = unif.borrow().get(&var).cloned();
                match looked_up {
                    // Apparently the environment is recursive; `{}`ing it stack-overflows
                    Some(ref clo) => {
                        format!("{} in some environment", clo.it /* , {:#?} clo.env */)
                    }
                    None => format!("¿{}?", var),
                }
            });
        }
        _ => {}
    }

    // TODO: this really ought to notice when `actl` is ill-formed for `pat`.
    match (pat, actl) {
        (&Named(name, ref body), _) => {
            unparse_mbe(&*body, context.get_leaf(name).unwrap_or(&Atom(n("<->"))), context, s)
        }
        //=> unparse_mbe(&*body, context.get_leaf(name).unwrap_or(&Atom(n("<MISSING>"))), context, s),
        (&Call(sub_form), _) => unparse_mbe(s.find_or_panic(&sub_form), actl, context, s),
        (&Anyways(_), _) | (&Impossible, _) => "".to_string(),
        (&Literal(_, n), _) => n.print(),
        (&Scan(_), &Atom(n)) => n.print(),
        (&Scan(_), _) => "".to_string(), // HACK for `Alt`
        (&VarRef(ref sub_form), &VariableReference(n)) => {
            unparse_mbe(&*sub_form, &Atom(n), context, s)
        }
        (&VarRef(_), _) => "".to_string(), // HACK for `Alt`
        (&Seq(ref sub_pats), _) => {
            let mut prev_empty = true;
            let mut res = String::new();
            for sub_pat in sub_pats {
                let sub_res = unparse_mbe(&*sub_pat, actl, context, s);
                if !prev_empty && sub_res != "" {
                    res.push(' ');
                }
                prev_empty = sub_res == "";
                res.push_str(&sub_res);
            }
            res
        }
        (&Alt(ref sub_pats), _) => {
            let mut any_scopes = false;
            for sub_pat in sub_pats {
                if let Scope(_, _) = &**sub_pat {
                    any_scopes = true;
                    continue;
                }

                let sub_res = unparse_mbe(&*sub_pat, actl, context, s);
                if sub_res != "" {
                    return sub_res;
                } // HACK: should use `Option`
            }
            // HACK: certain forms don't live in the syntax environment,
            //  but "belong" under an `Alt`, so just assume forms know their grammar:
            if any_scopes {
                if let &Node(ref form_actual, ref body, _) = actl {
                    return unparse_mbe(&*form_actual.grammar, actl, body, s);
                }
            }

            return "".to_string(); // Not sure if it's an error, or really just empty
        }
        (&Biased(ref lhs, ref rhs), _) => {
            format!("{}{}", unparse_mbe(lhs, actl, context, s), unparse_mbe(rhs, actl, context, s))
        }
        (&Star(ref sub_pat), _) | (&Plus(ref sub_pat), _) => {
            let mut first = true;
            let mut res = String::new();
            for marched_ctxt in context.march_all(&node_names_mentioned(&*sub_pat)) {
                if !first {
                    res.push(' ');
                }
                first = false;
                res.push_str(&unparse_mbe(&*sub_pat, actl, &marched_ctxt, s));
            }
            res
        }
        (&Scope(ref form, _), &Node(ref form_actual, ref body, _)) => {
            if form == form_actual {
                unparse_mbe(&*form.grammar, actl, body, s)
            } else {
                "".to_string() // HACK for `Alt`
            }
        }
        (&Scope(_, _), _) => "".to_string(), // Non-match
        (&Pick(ref body, _), _) | (&Common(ref body), _) => unparse_mbe(&*body, actl, context, s),
        (&NameImport(ref body, _), &ExtendEnv(ref actl_body, _)) => {
            unparse_mbe(&*body, &*actl_body, context, s)
        }
        (&NameImport(_, _), _) => format!("[Missing import]→{:#?}←", actl),
        (&NameImportPhaseless(ref body, _), &ExtendEnvPhaseless(ref actl_body, _)) => {
            unparse_mbe(&*body, &*actl_body, context, s)
        }
        (&NameImportPhaseless(_, _), _) => format!("[Missing import]±→{:#?}←±", actl),
        (&QuoteDeepen(ref body, _), &QuoteMore(ref actl_body, _)) => {
            unparse_mbe(&*body, &*actl_body, context, s)
        }
        (&QuoteDeepen(_, _), _) => format!("[Missing qm]{:#?}", actl),
        (&QuoteEscape(ref body, _), &QuoteLess(ref actl_body, _)) => {
            unparse_mbe(&*body, &*actl_body, context, s)
        }
        (&QuoteEscape(_, _), _) => format!("[Missing ql]{:#?}", actl),
        (&SynImport(ref _lhs_grammar, ref _rhs, _), &Node(_, _, _)) => {
            // TODO: I think we need to store the LHS or the new SynEnv to make this pretty.
            format!("?synax import? {:#?} ?si?", actl)
        }
        (&SynImport(_, _, _), _) => "".to_string(),
        (&Reserved(ref body, _), _) => unparse_mbe(body, actl, context, s),
    }
}
<commit_msg>Remove some noise from pretty-printing<commit_after>use crate::{
    ast::Ast::{self, *},
    grammar::{
        FormPat::{self, *},
        SynEnv,
    },
    name::*,
    util::mbe::EnvMBE,
};

fn node_names_mentioned(pat: &FormPat) -> Vec<Name> {
    match *pat {
        Named(n, ref body) => {
            let mut res = node_names_mentioned(&*body);
            res.push(n);
            res
        }
        Scope(_, _) => vec![],
        Pick(_, _) => vec![],
        Star(ref body)
        | Plus(ref body)
        | NameImport(ref body, _)
        | NameImportPhaseless(ref body, _)
        | VarRef(ref body)
        | Literal(ref body, _)
        | QuoteDeepen(ref body, _)
        | QuoteEscape(ref body, _)
        | Common(ref body)
        | Reserved(ref body, _) => node_names_mentioned(&*body),
        Seq(ref sub_pats) | Alt(ref sub_pats) => {
            let mut res = vec![];
            for pat in sub_pats {
                res.append(&mut node_names_mentioned(pat));
            }
            res
        }
        Biased(ref lhs, ref rhs) => {
            let mut res = node_names_mentioned(&*lhs);
            res.append(&mut node_names_mentioned(&*rhs));
            res
        }
        Anyways(_) | Impossible | Scan(_) | Call(_) | SynImport(_, _, _) => vec![],
    }
}

pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE<Ast>, s: &SynEnv) -> String {
    // HACK: handle underdetermined forms
    let undet = crate::ty_compare::underdetermined_form.with(|u| u.clone());
    match *actl {
        Node(ref form, ref body, _) if form == &undet => {
            return crate::ty_compare::unification.with(|unif| {
                let var = crate::core_forms::ast_to_name(body.get_leaf_or_panic(&n("id")));
                let looked_up = unif.borrow().get(&var).cloned();
                match looked_up {
                    // Apparently the environment is recursive; `{}`ing it stack-overflows
                    Some(ref clo) => {
                        format!("{} in some environment", clo.it /* , {:#?} clo.env */)
                    }
                    None => format!("¿{}?", var),
                }
            });
        }
        _ => {}
    }

    // TODO: this really ought to notice when `actl` is ill-formed for `pat`.
    match (pat, actl) {
        (&Named(name, ref body), _) => {
            // TODO: why does the `unwrap_or` case happen once after each variable is printed?
            unparse_mbe(&*body, context.get_leaf(name).unwrap_or(&Atom(n(""))), context, s)
        }
        (&Call(sub_form), _) => unparse_mbe(s.find_or_panic(&sub_form), actl, context, s),
        (&Anyways(_), _) | (&Impossible, _) => "".to_string(),
        (&Literal(_, n), _) => n.print(),
        (&Scan(_), &Atom(n)) => n.print(),
        (&Scan(_), _) => "".to_string(), // HACK for `Alt`
        (&VarRef(ref sub_form), &VariableReference(n)) => {
            unparse_mbe(&*sub_form, &Atom(n), context, s)
        }
        (&VarRef(_), _) => "".to_string(), // HACK for `Alt`
        (&Seq(ref sub_pats), _) => {
            let mut prev_empty = true;
            let mut res = String::new();
            for sub_pat in sub_pats {
                let sub_res = unparse_mbe(&*sub_pat, actl, context, s);
                if !prev_empty && sub_res != "" {
                    res.push(' ');
                }
                prev_empty = sub_res == "";
                res.push_str(&sub_res);
            }
            res
        }
        (&Alt(ref sub_pats), _) => {
            let mut any_scopes = false;
            for sub_pat in sub_pats {
                if let Scope(_, _) = &**sub_pat {
                    any_scopes = true;
                    continue;
                }

                let sub_res = unparse_mbe(&*sub_pat, actl, context, s);
                if sub_res != "" {
                    return sub_res;
                } // HACK: should use `Option`
            }
            // HACK: certain forms don't live in the syntax environment,
            //  but "belong" under an `Alt`, so just assume forms know their grammar:
            if any_scopes {
                if let &Node(ref form_actual, ref body, _) = actl {
                    return unparse_mbe(&*form_actual.grammar, actl, body, s);
                }
            }

            return "".to_string(); // Not sure if it's an error, or really just empty
        }
        (&Biased(ref lhs, ref rhs), _) => {
            format!("{}{}", unparse_mbe(lhs, actl, context, s), unparse_mbe(rhs, actl, context, s))
        }
        (&Star(ref sub_pat), _) | (&Plus(ref sub_pat), _) => {
            let mut first = true;
            let mut res = String::new();
            for marched_ctxt in context.march_all(&node_names_mentioned(&*sub_pat)) {
                if !first {
                    res.push(' ');
                }
                first = false;
                res.push_str(&unparse_mbe(&*sub_pat, actl, &marched_ctxt, s));
            }
            res
        }
        (&Scope(ref form, _), &Node(ref form_actual, ref body, _)) => {
            if form == form_actual {
                unparse_mbe(&*form.grammar, actl, body, s)
            } else {
                "".to_string() // HACK for `Alt`
            }
        }
        (&Scope(_, _), _) => "".to_string(), // Non-match
        (&Pick(ref body, _), _) | (&Common(ref body), _) => unparse_mbe(&*body, actl, context, s),
        (&NameImport(ref body, _), &ExtendEnv(ref actl_body, _)) => {
            unparse_mbe(&*body, &*actl_body, context, s)
        }
        (&NameImport(_, _), _) => format!("[Missing import]→{:#?}←", actl),
        (&NameImportPhaseless(ref body, _), &ExtendEnvPhaseless(ref actl_body, _)) => {
            unparse_mbe(&*body, &*actl_body, context, s)
        }
        (&NameImportPhaseless(_, _), _) => format!("[Missing import]±→{:#?}←±", actl),
        (&QuoteDeepen(ref body, _), &QuoteMore(ref actl_body, _)) => {
            unparse_mbe(&*body, &*actl_body, context, s)
        }
        (&QuoteDeepen(_, _), _) => format!("[Missing qm]{:#?}", actl),
        (&QuoteEscape(ref body, _), &QuoteLess(ref actl_body, _)) => {
            unparse_mbe(&*body, &*actl_body, context, s)
        }
        (&QuoteEscape(_, _), _) => format!("[Missing ql]{:#?}", actl),
        (&SynImport(ref _lhs_grammar, ref _rhs, _), &Node(_, _, _)) => {
            // TODO: I think we need to store the LHS or the new SynEnv to make this pretty.
            format!("?synax import? {:#?} ?si?", actl)
        }
        (&SynImport(_, _, _), _) => "".to_string(),
        (&Reserved(ref body, _), _) => unparse_mbe(body, actl, context, s),
    }
}
<|endoftext|>


<commit_before>
pub type Vector3 = [f64, ..3];
pub type Matrix4x3 = [[f64, ..3], ..4];

#[inline(always)]
pub fn vec3_sub(a: Vector3, b: Vector3) -> Vector3 {
    [
        a[0] - b[0],
        a[1] - b[1],
        a[2] - b[2],
    ]
}

#[inline(always)]
pub fn vec3_len(a: Vector3) -> f64 {
    a[0] * a[0] + a[1] * a[1] + a[2] * a[2]
}

#[inline(always)]
pub fn vec3_inv_len(a: Vector3) -> f64 {
    1.0 / vec3_len(a)
}

#[inline(always)]
pub fn vec3_normalized(a: Vector3) -> Vector3 {
    let inv_len = vec3_inv_len(a);
    [
        a[0] * inv_len,
        a[1] * inv_len,
        a[2] * inv_len,
    ]
}

#[inline(always)]
pub fn vec3_normalized_sub(a: Vector3, b: Vector3) -> Vector3 {
    vec3_normalized(vec3_sub(a, b))
}

<commit_msg>Added `mat4x3_transform`<commit_after>
pub type Vector3 = [f64, ..3];
pub type Matrix4x3 = [[f64, ..3], ..4];

#[inline(always)]
pub fn vec3_sub(a: Vector3, b: Vector3) -> Vector3 {
    [
        a[0] - b[0],
        a[1] - b[1],
        a[2] - b[2],
    ]
}

#[inline(always)]
pub fn vec3_len(a: Vector3) -> f64 {
    a[0] * a[0] + a[1] * a[1] + a[2] * a[2]
}

#[inline(always)]
pub fn vec3_inv_len(a: Vector3) -> f64 {
    1.0 / vec3_len(a)
}

#[inline(always)]
pub fn vec3_normalized(a: Vector3) -> Vector3 {
    let inv_len = vec3_inv_len(a);
    [
        a[0] * inv_len,
        a[1] * inv_len,
        a[2] * inv_len,
    ]
}

#[inline(always)]
pub fn vec3_normalized_sub(a: Vector3, b: Vector3) -> Vector3 {
    vec3_normalized(vec3_sub(a, b))
}

#[inline(always)]
pub fn mat4x3_transform(mat: Matrix4x3, a: Vector3) -> Vector3 {
    [
        mat[0][0] * a[0] + mat[1][0] * a[1] + mat[2][0] * a[2] + mat[3][0],
        mat[0][1] * a[0] + mat[1][1] * a[1] + mat[2][1] * a[2] + mat[3][1],
        mat[0][2] * a[0] + mat[1][2] * a[1] + mat[2][2] * a[2] + mat[3][2],
    ]
}

<|endoftext|>


<commit_before>use std::io::Read;
use std::env;
use hyper::Client;
use hyper::header::Connection;
use std::collections::BTreeMap;
use rustc_serialize::json;
use std::fs::File;

static BASE_URL:    &'static str = "https://www.googleapis.com/youtube/v3";
static MAX_RESULTS: i32          = 50;
lazy_static! {
    static ref API_KEY: String = {
        let opt_key = env::var("YOUTUBE_API_KEY");
        match opt_key {
            Ok(key) => key,
            Err(_) => {
                let mut f = File::open("youtube.txt").unwrap();
                let mut s = String::new();
                let _ = f.read_to_string(&mut s);
                s
            }
        }
    };
}

pub trait HasThumbnail {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail>;
    fn get_thumbnail_url(&self) -> Option<String> {
        self.get_thumbnails().get(        "default" ).map(|t| t.url.to_string())
            .or(self.get_thumbnails().get("medium"  ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("high"    ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("standard").map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("maxres"  ).map(|t| t.url.to_string()))
    }
    fn get_artwork_url(&self) -> Option<String> {
        self.get_thumbnails().get(        "maxres"  ).map(|t| t.url.to_string())
            .or(self.get_thumbnails().get("standard").map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("high"    ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("medium"  ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("default" ).map(|t| t.url.to_string()))
    }
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Thumbnail {
    pub url:    String,
    pub width:  i32,
    pub height: i32
}

impl PartialEq for Thumbnail {
    fn eq(&self, t: &Thumbnail) -> bool {
        return self.url == t.url;
    }
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemResponse {
    pub kind:          String,
    pub etag:          String,
    pub nextPageToken: Option<String>,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<PlaylistItem>,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItem {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: PlaylistItemSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemSnippet {
    pub title:         String,
    pub description:   String,
    pub publishedAt:   String,
    pub channelId:     String,
    pub channelTitle:  String,
    pub thumbnails:    BTreeMap<String, Thumbnail>,
    pub position:      i32,
    pub playlistId:    String,
    pub resourceId:    BTreeMap<String, String>
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct VideoResponse {
    pub kind:          String,
    pub etag:          String,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<Video>,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Video {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: VideoSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct VideoSnippet {
    pub title:                String,
    pub description:          String,
    pub publishedAt:          String,
    pub channelId:            String,
    pub channelTitle:         String,
    pub thumbnails:           BTreeMap<String, Thumbnail>,
    pub tags:                 Option<Vec<String>>,
    pub categoryId:           String,
    pub liveBroadcastContent: String,
}

impl HasThumbnail for PlaylistItemSnippet {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail> {
        &self.thumbnails
    }
}

impl HasThumbnail for VideoSnippet {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail> {
        &self.thumbnails
    }
}

pub fn fetch_playlist(id: &str) -> json::DecodeResult<PlaylistItemResponse> {
    let params = format!("key={}&part=snippet&playlistId={}&maxResults={}",
                         *API_KEY,
                         id,
                         MAX_RESULTS);
    let url    = format!("{}/{}?{}", BASE_URL, "playlistItems", params);
    let client = Client::new();
    let mut res = client.get(&url)
                        .header(Connection::close())
                        .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    json::decode::<PlaylistItemResponse>(&body)
}

pub fn fetch_video(id: &str) -> json::DecodeResult<Video> {
    let params = format!("key={}&part=snippet&id={}", *API_KEY, id);
    let url    = format!("{}/{}?{}", BASE_URL, "videos", params);
    let client = Client::new();
    let mut res = client.get(&url)
                        .header(Connection::close())
                        .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    let res = try!(json::decode::<VideoResponse>(&body));
    if res.items.len() > 0 {
        return Ok(res.items[0].clone());
    }
    Err(json::DecoderError::ApplicationError("track not found".to_string()))
}
<commit_msg>YouTube resource may have no thumbnails<commit_after>use std::io::Read;
use std::env;
use hyper::Client;
use hyper::header::Connection;
use std::collections::BTreeMap;
use rustc_serialize::json;
use std::fs::File;

static BASE_URL:    &'static str = "https://www.googleapis.com/youtube/v3";
static MAX_RESULTS: i32          = 50;
lazy_static! {
    static ref API_KEY: String = {
        let opt_key = env::var("YOUTUBE_API_KEY");
        match opt_key {
            Ok(key) => key,
            Err(_) => {
                let mut f = File::open("youtube.txt").unwrap();
                let mut s = String::new();
                let _ = f.read_to_string(&mut s);
                s
            }
        }
    };
}

pub trait HasThumbnail {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail>;
    fn get_thumbnail_url(&self) -> Option<String> {
        let thumbs = self.get_thumbnails();
        thumbs.get(         "default").map(|t| t.url.to_string())
            .or(thumbs.get("medium"  ).map(|t| t.url.to_string()))
            .or(thumbs.get("high"    ).map(|t| t.url.to_string()))
            .or(thumbs.get("standard").map(|t| t.url.to_string()))
            .or(thumbs.get("maxres"  ).map(|t| t.url.to_string()))
    }
    fn get_artwork_url(&self) -> Option<String> {
        let thumbs = self.get_thumbnails();
        thumbs.get(          "maxres").map(|t| t.url.to_string())
            .or(thumbs.get("standard").map(|t| t.url.to_string()))
            .or(thumbs.get("high"    ).map(|t| t.url.to_string()))
            .or(thumbs.get("medium"  ).map(|t| t.url.to_string()))
            .or(thumbs.get("default" ).map(|t| t.url.to_string()))
    }
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Thumbnail {
    pub url:    String,
    pub width:  i32,
    pub height: i32
}

impl PartialEq for Thumbnail {
    fn eq(&self, t: &Thumbnail) -> bool {
        return self.url == t.url;
    }
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemResponse {
    pub kind:          String,
    pub etag:          String,
    pub nextPageToken: Option<String>,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<PlaylistItem>,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItem {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: PlaylistItemSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemSnippet {
    pub title:         String,
    pub description:   String,
    pub publishedAt:   String,
    pub channelId:     String,
    pub channelTitle:  String,
    pub thumbnails:    Option<BTreeMap<String, Thumbnail>>,
    pub position:      i32,
    pub playlistId:    String,
    pub resourceId:    BTreeMap<String, String>
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct VideoResponse {
    pub kind:          String,
    pub etag:          String,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<Video>,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Video {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: VideoSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct VideoSnippet {
    pub title:                String,
    pub description:          String,
    pub publishedAt:          String,
    pub channelId:            String,
    pub channelTitle:         String,
    pub thumbnails:           Option<BTreeMap<String, Thumbnail>>,
    pub tags:                 Option<Vec<String>>,
    pub categoryId:           String,
    pub liveBroadcastContent: String,
}

impl HasThumbnail for PlaylistItemSnippet {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail> {
        self.thumbnails.clone().unwrap_or(BTreeMap::new())
    }
}

impl HasThumbnail for VideoSnippet {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail> {
        self.thumbnails.clone().unwrap_or(BTreeMap::new())
    }
}

pub fn fetch_playlist(id: &str) -> json::DecodeResult<PlaylistItemResponse> {
    let params = format!("key={}&part=snippet&playlistId={}&maxResults={}",
                         *API_KEY,
                         id,
                         MAX_RESULTS);
    let url    = format!("{}/{}?{}", BASE_URL, "playlistItems", params);
    let client = Client::new();
    let mut res = client.get(&url)
                        .header(Connection::close())
                        .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    json::decode::<PlaylistItemResponse>(&body)
}

pub fn fetch_video(id: &str) -> json::DecodeResult<Video> {
    let params = format!("key={}&part=snippet&id={}", *API_KEY, id);
    let url    = format!("{}/{}?{}", BASE_URL, "videos", params);
    let client = Client::new();
    let mut res = client.get(&url)
                        .header(Connection::close())
                        .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    let res = try!(json::decode::<VideoResponse>(&body));
    if res.items.len() > 0 {
        return Ok(res.items[0].clone());
    }
    Err(json::DecoderError::ApplicationError("track not found".to_string()))
}
<|endoftext|>