accepted 0.3.2

A text editor to be ACCEPTED.
Documentation
use crate::core::CoreBuffer;
use anyhow::Context;
use lsp_types::{CompletionItemKind, Documentation};
use serde_derive::{Deserialize, Serialize};
use std::process;
use tokio::prelude::*;
use tokio::stream::StreamExt;

pub struct TabNineClient {
    _proc: tokio::process::Child,
    results_receiver: tokio::sync::mpsc::UnboundedReceiver<AutocompleteResponse>,
    args_sender: tokio::sync::mpsc::UnboundedSender<AutocompleteArgs>,
}

pub struct TabNineCompletion {
    pub keyword: String,
    pub doc: String,
    pub old_prefix: String,
}

impl TabNineClient {
    pub fn new(command: std::process::Command) -> anyhow::Result<Self> {
        let mut proc: tokio::process::Child = tokio::process::Command::from(command)
            .stdin(process::Stdio::piped())
            .stderr(process::Stdio::piped())
            .stdout(process::Stdio::piped())
            .kill_on_drop(true)
            .spawn()?;

        let stdout = proc.stdout.take().context("get stdout")?;
        let mut stdin = proc.stdin.take().context("get stdin")?;

        let (args_sender, mut args_receiver) = tokio::sync::mpsc::unbounded_channel();
        let (results_sender, results_receiver) = tokio::sync::mpsc::unbounded_channel();

        tokio::spawn(async move {
            while let Some(args) = args_receiver.recv().await {
                let req = Request {
                    version: "1.0.0".to_string(),
                    request: AutoComplete { autocomplete: args },
                };
                if let Ok(json) = serde_json::to_string(&req) {
                    let json = json.replace('\n', "") + "\n";
                    let _ = stdin.write_all(json.as_bytes()).await;
                    let _ = stdin.flush().await;
                }
            }
        });

        tokio::spawn(async move {
            let mut lines = tokio::io::BufReader::new(stdout).lines();
            while let Some(Ok(line)) = lines.next().await {
                if let Ok(res) = serde_json::from_str::<AutocompleteResponse>(line.as_str()) {
                    if results_sender.send(res).is_err() {
                        return;
                    }
                }
            }
        });

        Ok(TabNineClient {
            _proc: proc,
            results_receiver,
            args_sender,
        })
    }

    pub fn poll(&mut self) -> Option<Vec<TabNineCompletion>> {
        let mut ret = None;
        while let Ok(res) = self.results_receiver.try_recv() {
            let old_prefix = res.old_prefix.clone();
            ret = Some(
                res.results
                    .into_iter()
                    .map(|mut entry| TabNineCompletion {
                        keyword: entry.new_prefix.clone(),
                        doc: entry
                            .documentation
                            .take()
                            .map(|d| match d {
                                Documentation::String(s) => s,
                                Documentation::MarkupContent(m) => m.value,
                            })
                            .unwrap_or_else(|| "TabNine".to_string()),
                        old_prefix: old_prefix.clone(),
                    })
                    .collect::<Vec<_>>(),
            );
        }
        ret
    }

    pub fn request_completion<B: CoreBuffer>(&self, buf: &crate::Buffer<B>) {
        let before = buf.core.core_buffer().get_range(..buf.core.cursor());
        let after = buf.core.core_buffer().get_range(buf.core.cursor()..);

        let args = AutocompleteArgs {
            before,
            after,
            filename: buf.path().map(|p| p.to_string_lossy().into_owned()),
            region_includes_beginning: true,
            region_includes_end: true,
            max_num_results: None,
        };
        let _ = self.args_sender.send(args);
    }
}

#[derive(Serialize, Deserialize)]
struct Request {
    version: String,
    request: AutoComplete,
}

#[derive(Serialize, Deserialize)]
struct AutoComplete {
    #[serde(rename = "Autocomplete")]
    autocomplete: AutocompleteArgs,
}

#[derive(Serialize, Deserialize, Debug)]
struct AutocompleteArgs {
    before: String,
    after: String,
    filename: Option<String>,
    region_includes_beginning: bool,
    region_includes_end: bool,
    max_num_results: Option<usize>,
}

#[derive(Serialize, Deserialize)]
struct AutocompleteResponse {
    old_prefix: String,
    results: Vec<ResultEntry>,
}

#[derive(Serialize, Deserialize)]
struct ResultEntry {
    new_prefix: String,
    old_suffix: String,
    new_suffix: String,

    kind: Option<CompletionItemKind>,
    detail: Option<String>,
    documentation: Option<Documentation>,
    deprecated: Option<bool>,
}