#![warn(missing_debug_implementations, missing_docs)]
use std::{
env,
ops::Range,
sync::atomic::{AtomicUsize, Ordering},
};
use json::JsonValue;
static COMMAND_INDEX: AtomicUsize = AtomicUsize::new(0);
fn next_command_index() -> usize {
COMMAND_INDEX.fetch_add(1, Ordering::SeqCst)
}
fn send_command(command: JsonValue) {
if let Ok(value) = env::var("DEVIZ_SERVER") {
if value.trim() == "1" {
let json_text = JsonValue::from(vec![command]).to_string();
eprint!("|DEVIZ:BEGIN|{}|DEVIZ:END|", json_text);
}
}
}
pub fn text(pane_name: impl Into<String>, text: impl Into<String>) -> Text {
Text::new(next_command_index(), pane_name.into(), text.into())
}
#[derive(Debug)]
pub struct Text {
command_index: usize,
pane_name: String,
text: String,
hovers: Vec<Hover>,
}
impl Text {
fn new(command_index: usize, pane_name: String, text: String) -> Self {
Self {
command_index,
pane_name,
text,
hovers: Vec::new(),
}
}
fn json(&self) -> JsonValue {
json::object! {
index: self.command_index,
pane: {
name: self.pane_name.clone(),
content: {
type: "text",
data: {
text: self.text.clone(),
hovers: self.hovers.clone(),
},
},
},
}
}
pub fn hover_text(&mut self, range: Range<usize>, text: impl Into<String>) {
self.hovers.push(Hover {
start: range.start,
end: range.end,
text: text.into(),
});
}
}
impl Drop for Text {
fn drop(&mut self) {
send_command(self.json());
}
}
#[derive(Debug, Clone)]
struct Hover {
start: usize,
end: usize,
text: String,
}
impl From<Hover> for JsonValue {
fn from(hover: Hover) -> Self {
json::object! {
start: hover.start,
end: hover.end,
text: hover.text,
}
}
}
pub fn tree(pane_name: impl Into<String>) -> Tree {
Tree::new(next_command_index(), pane_name.into(), TreeKind::Tree)
}
pub fn text_tree(pane_name: impl Into<String>) -> Tree {
Tree::new(next_command_index(), pane_name.into(), TreeKind::TextTree)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum TreeKind {
Tree,
TextTree,
}
#[derive(Debug)]
pub struct Tree {
command_index: usize,
pane_name: String,
kind: TreeKind,
roots: Vec<TreeNode>,
stack: Vec<TreeNode>,
}
impl Tree {
fn new(command_index: usize, pane_name: String, kind: TreeKind) -> Self {
Self {
command_index,
pane_name,
kind,
roots: Vec::new(),
stack: Vec::new(),
}
}
fn json(&mut self) -> JsonValue {
while !self.stack.is_empty() {
self.end_node();
}
let pane_type = match self.kind {
TreeKind::Tree => "tree",
TreeKind::TextTree => "text_tree",
};
json::object! {
index: self.command_index,
pane: {
name: self.pane_name.clone(),
content: {
type: pane_type,
data: self.roots.clone(),
},
},
}
}
pub fn begin_node(&mut self) {
self.stack.push(TreeNode {
label: None,
children: Vec::new(),
});
}
pub fn end_node(&mut self) {
let node = self
.stack
.pop()
.expect("mismatched begin_node/end_node calls");
match self.stack.last_mut() {
Some(parent) => parent.children.push(node),
None => self.roots.push(node),
}
}
pub fn label(&mut self, label: impl Into<String>) {
let node = self
.stack
.last_mut()
.expect("label must be called between begin_node and end_node");
node.label = Some(label.into());
}
}
impl Drop for Tree {
fn drop(&mut self) {
send_command(self.json());
}
}
#[derive(Debug, Clone)]
struct TreeNode {
label: Option<String>,
children: Vec<TreeNode>,
}
impl From<TreeNode> for JsonValue {
fn from(node: TreeNode) -> Self {
json::object! {
label: node.label,
children: node.children,
}
}
}
pub fn graph(pane_name: impl Into<String>) -> Graph {
Graph::new(next_command_index(), pane_name.into())
}
#[derive(Debug)]
pub struct Graph {
command_index: usize,
pane_name: String,
nodes: Vec<GraphNode>,
edges: Vec<GraphEdge>,
}
impl Graph {
fn new(command_index: usize, pane_name: String) -> Self {
Self {
command_index,
pane_name,
nodes: Vec::new(),
edges: Vec::new(),
}
}
fn json(&self) -> JsonValue {
json::object! {
index: self.command_index,
pane: {
name: self.pane_name.clone(),
content: {
type: "graph",
data: [{
nodes: self.nodes.clone(),
edges: self.edges.clone(),
}],
}
},
}
}
pub fn node(&mut self, id: impl Into<String>) {
self.nodes.push(GraphNode {
id: id.into(),
label: None,
});
}
pub fn node_labeled(&mut self, id: impl Into<String>, label: impl Into<String>) {
self.nodes.push(GraphNode {
id: id.into(),
label: Some(label.into()),
});
}
pub fn edge(&mut self, from_id: impl Into<String>, to_id: impl Into<String>) {
self.edges.push(GraphEdge {
from_id: from_id.into(),
to_id: to_id.into(),
label: None,
});
}
pub fn edge_labeled(
&mut self,
from_id: impl Into<String>,
to_id: impl Into<String>,
label: impl Into<String>,
) {
self.edges.push(GraphEdge {
from_id: from_id.into(),
to_id: to_id.into(),
label: Some(label.into()),
});
}
}
impl Drop for Graph {
fn drop(&mut self) {
send_command(self.json());
}
}
#[derive(Debug, Clone)]
struct GraphNode {
id: String,
label: Option<String>,
}
impl From<GraphNode> for JsonValue {
fn from(node: GraphNode) -> Self {
json::object! {
id: node.id,
label: node.label,
}
}
}
#[derive(Debug, Clone)]
struct GraphEdge {
from_id: String,
to_id: String,
label: Option<String>,
}
impl From<GraphEdge> for JsonValue {
fn from(edge: GraphEdge) -> Self {
json::object! {
fromId: edge.from_id,
toId: edge.to_id,
label: edge.label,
}
}
}