mirror of
https://github.com/SpaceManiac/SpacemanDMM.git
synced 2025-12-23 05:36:47 +00:00
Add documentation to hovers, add hovers for var uses and proc calls (#210)
- Added hover handling for ScopedVar, UnscopedVar, ScopedProc, UnscopedProc - Improved handling of existing hovers, adding dm lang blocks to the hovers where appropriate - Added documentation to all existing hovers when it is available - Fixed bug with annotating Variables, which would lead to excessive whitespace being consumed
This commit is contained in:
parent
6bef4545f4
commit
985da485e2
2 changed files with 141 additions and 52 deletions
|
|
@ -786,17 +786,22 @@ impl<'ctx, 'an, 'inp> Parser<'ctx, 'an, 'inp> {
|
|||
Punct(Assign) => {
|
||||
// `something=` - var
|
||||
let location = self.location;
|
||||
|
||||
// kind of goofy, but allows "enclosing" doc comments at the end of the line
|
||||
// translators note: this allows comments of the form ``//! blah`` at the end of the line
|
||||
let (docs, expression) = require!(self.doc_comment(|this| {
|
||||
let expr = require!(this.expression());
|
||||
let _ = require!(this.input_specifier());
|
||||
|
||||
// We have to annotate prior to consuming the statement terminator, as we
|
||||
// will otherwise consume following whitespace resulting in a bad annotation range
|
||||
let node = this.tree[current].path.to_owned();
|
||||
this.annotate(entry_start, || Annotation::Variable(reconstruct_path(&node, proc_kind, var_type.as_ref(), last_part)));
|
||||
|
||||
require!(this.statement_terminator());
|
||||
success(expr)
|
||||
}));
|
||||
|
||||
let node = self.tree[current].path.to_owned();
|
||||
self.annotate(entry_start, || Annotation::Variable(reconstruct_path(&node, proc_kind, var_type.as_ref(), last_part)));
|
||||
|
||||
if let Some(mut var_type) = var_type {
|
||||
var_type.suffix(&var_suffix);
|
||||
self.tree.declare_var(current, last_part, location, docs, var_type, Some(expression));
|
||||
|
|
|
|||
|
|
@ -840,6 +840,91 @@ impl<'a> Engine<'a> {
|
|||
Ok(symbol_id)
|
||||
}
|
||||
|
||||
fn construct_proc_hover(&self, proc_name: &str, mut provided_tok: Option<TypeRef>, scoped: bool) -> Result<Vec<String>, jsonrpc::Error> {
|
||||
let mut results = Vec::new();
|
||||
let mut proclink = String::new();
|
||||
let mut defstring = String::new();
|
||||
let mut docstring: Option<String> = None;
|
||||
while let Some(ty) = provided_tok {
|
||||
if let Some(proc) = ty.procs.get(proc_name) {
|
||||
let proc_value = proc.main_value();
|
||||
|
||||
// Because we need to find our declaration to get the declaration type, we partially
|
||||
// form the markdown text to be used once the proc's declaration is reached
|
||||
if defstring.is_empty() {
|
||||
proclink = format!("[{}]({})", ty.pretty_path(), self.location_link(proc_value.location)?);
|
||||
let mut message = format!("{}(", proc_name);
|
||||
let mut first = true;
|
||||
for each in proc_value.parameters.iter() {
|
||||
use std::fmt::Write;
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
message.push_str(", ");
|
||||
}
|
||||
let _ = write!(message, "{}", each);
|
||||
}
|
||||
message.push_str(")");
|
||||
defstring = message.clone();
|
||||
}
|
||||
|
||||
if let Some(ref decl) = proc.declaration {
|
||||
results.push(format!("{}\n```dm\n{}/{}\n```", proclink, decl.kind.name(), defstring));
|
||||
}
|
||||
|
||||
if !proc_value.docs.is_empty() {
|
||||
docstring = Some(proc_value.docs.text());
|
||||
}
|
||||
}
|
||||
|
||||
if scoped {
|
||||
provided_tok = ty.parent_type_without_root();
|
||||
} else {
|
||||
provided_tok = ty.parent_type();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ds) = docstring {
|
||||
results.push(ds);
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn construct_var_hover(&self, var_name: &str, mut provided_tok: Option<TypeRef>, scoped: bool) -> Result<Vec<String>, jsonrpc::Error> {
|
||||
let mut results = Vec::new();
|
||||
let mut infos = String::new();
|
||||
let mut docstring: Option<String> = None;
|
||||
while let Some(ty) = provided_tok {
|
||||
if let Some(var) = ty.vars.get(var_name) {
|
||||
if let Some(ref decl) = var.declaration {
|
||||
// First get the path of the type containing the declaration
|
||||
infos.push_str(format!("[{}]({})\n", ty.pretty_path(), self.location_link(var.value.location)?).as_str());
|
||||
|
||||
// Next toss on the declaration itself
|
||||
infos.push_str(format!("```dm\nvar/{}{}\n```", decl.var_type, var_name).as_str());
|
||||
}
|
||||
if !var.value.docs.is_empty() {
|
||||
docstring = Some(var.value.docs.text());
|
||||
}
|
||||
}
|
||||
|
||||
if scoped {
|
||||
provided_tok = ty.parent_type_without_root();
|
||||
} else {
|
||||
provided_tok = ty.parent_type();
|
||||
}
|
||||
}
|
||||
if !infos.is_empty() {
|
||||
results.push(infos);
|
||||
}
|
||||
if let Some(ds) = docstring {
|
||||
results.push(ds);
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Driver
|
||||
|
||||
|
|
@ -1096,9 +1181,11 @@ handle_method_call! {
|
|||
line: tdp.position.line as u32 + 1,
|
||||
column: tdp.position.character as u16 + 1,
|
||||
};
|
||||
let symbol_id = self.symbol_id_at(tdp)?;
|
||||
let mut results = Vec::new();
|
||||
|
||||
for (_range, annotation) in annotations.get_location(location) {
|
||||
let iter = annotations.get_location(location);
|
||||
for (_range, annotation) in iter.clone() {
|
||||
#[cfg(debug_assertions)] {
|
||||
results.push(format!("{:?}", annotation));
|
||||
}
|
||||
|
|
@ -1118,48 +1205,20 @@ handle_method_call! {
|
|||
|
||||
let mut infos = VecDeque::new();
|
||||
let mut next = Some(current);
|
||||
let mut docstring: Option<String> = None;
|
||||
while let Some(current) = next {
|
||||
if let Some(var) = current.vars.get(last) {
|
||||
let constant = if let Some(ref constant) = var.value.constant {
|
||||
format!(" \n= `{}`", constant)
|
||||
format!("\n```dm\n= {}\n```", constant)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let path = if current.path.is_empty() {
|
||||
"(global)"
|
||||
} else {
|
||||
¤t.path
|
||||
};
|
||||
infos.push_front(format!("[{}]({}){}", path, self.location_link(var.value.location)?, constant));
|
||||
infos.push_front(format!("[{}]({}){}", current.pretty_path(), self.location_link(var.value.location)?, constant));
|
||||
if let Some(ref decl) = var.declaration {
|
||||
let mut declaration = String::new();
|
||||
declaration.push_str("var");
|
||||
if decl.var_type.flags.is_static() {
|
||||
declaration.push_str("/static");
|
||||
}
|
||||
if decl.var_type.flags.is_const() {
|
||||
declaration.push_str("/const");
|
||||
}
|
||||
if decl.var_type.flags.is_tmp() {
|
||||
declaration.push_str("/tmp");
|
||||
}
|
||||
if decl.var_type.flags.is_final() {
|
||||
declaration.push_str("/SpacemanDMM_final");
|
||||
}
|
||||
if decl.var_type.flags.is_private() {
|
||||
declaration.push_str("/SpacemanDMM_private");
|
||||
}
|
||||
if decl.var_type.flags.is_protected() {
|
||||
declaration.push_str("/SpacemanDMM_protected");
|
||||
}
|
||||
for bit in decl.var_type.type_path.iter() {
|
||||
declaration.push('/');
|
||||
declaration.push_str(&bit);
|
||||
}
|
||||
declaration.push_str("/**");
|
||||
declaration.push_str(last);
|
||||
declaration.push_str("**");
|
||||
infos.push_front(declaration);
|
||||
infos.push_front(format!("```dm\nvar/{}{}\n```", decl.var_type, last));
|
||||
}
|
||||
if !var.value.docs.is_empty() {
|
||||
docstring = Some(var.value.docs.text());
|
||||
}
|
||||
}
|
||||
next = current.parent_type();
|
||||
|
|
@ -1167,6 +1226,9 @@ handle_method_call! {
|
|||
if !infos.is_empty() {
|
||||
results.push(infos.into_iter().collect::<Vec<_>>().join("\n\n"));
|
||||
}
|
||||
if let Some(ds) = docstring {
|
||||
results.push(ds);
|
||||
}
|
||||
}
|
||||
Annotation::ProcHeader(path, _idx) if !path.is_empty() => {
|
||||
let objtree = &self.objtree;
|
||||
|
|
@ -1185,15 +1247,11 @@ handle_method_call! {
|
|||
// the last proc for each type
|
||||
let mut infos = VecDeque::new();
|
||||
let mut next = Some(current);
|
||||
let mut docstring: Option<String> = None;
|
||||
while let Some(current) = next {
|
||||
if let Some(proc) = current.procs.get(last) {
|
||||
let path = if current.path.is_empty() {
|
||||
"(global)"
|
||||
} else {
|
||||
¤t.path
|
||||
};
|
||||
let proc_value = proc.main_value();
|
||||
let mut message = format!("[{}]({}) \n{}(", path, self.location_link(proc_value.location)?, last);
|
||||
let mut message = format!("[{}]({}) \n```dm\n{}(", current.pretty_path(), self.location_link(proc_value.location)?, last);
|
||||
let mut first = true;
|
||||
for each in proc_value.parameters.iter() {
|
||||
use std::fmt::Write;
|
||||
|
|
@ -1204,15 +1262,14 @@ handle_method_call! {
|
|||
}
|
||||
let _ = write!(message, "{}", each);
|
||||
}
|
||||
message.push_str(")");
|
||||
message.push_str(")\n```");
|
||||
infos.push_front(message);
|
||||
if let Some(ref decl) = proc.declaration {
|
||||
let mut declaration = String::new();
|
||||
declaration.push_str(decl.kind.name());
|
||||
declaration.push_str("/**");
|
||||
declaration.push_str(last);
|
||||
declaration.push_str("**");
|
||||
infos.push_front(declaration);
|
||||
infos.push_front(format!("```dm\n{}/{}\n```", decl.kind.name(), last));
|
||||
}
|
||||
|
||||
if !proc_value.docs.is_empty() {
|
||||
docstring = Some(proc_value.docs.text());
|
||||
}
|
||||
}
|
||||
next = current.parent_type();
|
||||
|
|
@ -1220,6 +1277,33 @@ handle_method_call! {
|
|||
if !infos.is_empty() {
|
||||
results.push(infos.into_iter().collect::<Vec<_>>().join("\n\n"));
|
||||
}
|
||||
if let Some(ds) = docstring {
|
||||
results.push(ds);
|
||||
}
|
||||
}
|
||||
Annotation::UnscopedVar(var_name) if symbol_id.is_some() => {
|
||||
let (ty, proc_name) = self.find_type_context(&iter);
|
||||
match self.find_unscoped_var(&iter, ty, proc_name, var_name) {
|
||||
UnscopedVar::Variable { ty, .. } => {
|
||||
if let Some(_decl) = ty.get_var_declaration(var_name) {
|
||||
results.append(&mut self.construct_var_hover(var_name, Some(ty), false)?);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Annotation::UnscopedCall(proc_name) if symbol_id.is_some() => {
|
||||
let (ty, _) = self.find_type_context(&iter);
|
||||
let next = ty.or(Some(self.objtree.root()));
|
||||
results.append(&mut self.construct_proc_hover(proc_name, next, false)?);
|
||||
}
|
||||
Annotation::ScopedCall(priors, proc_name) if symbol_id.is_some() => {
|
||||
let next = self.find_scoped_type(&iter, priors);
|
||||
results.append(&mut self.construct_proc_hover(proc_name, next, true)?);
|
||||
}
|
||||
Annotation::ScopedVar(priors, var_name) if symbol_id.is_some() => {
|
||||
let next = self.find_scoped_type(&iter, priors);
|
||||
results.append(&mut self.construct_var_hover(var_name, next, true)?);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue