Added formatting for module completion

This commit is contained in:
Eli Dowling 2024-02-11 14:24:33 +10:00 committed by Eli Dowling
parent c6f1b8b0b2
commit 76a2fd2fb0
4 changed files with 117 additions and 23 deletions

View File

@ -45,6 +45,7 @@ pub(super) struct AnalyzedModule {
interns: Interns,
subs: Subs,
other_modules_subs: Arc<Mutex<HashMap<ModuleId, Subs>>>,
modules_exposed: Arc<Mutex<HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>>>,
abilities: AbilitiesStore,
declarations: Declarations,
// We need this because ModuleIds are not stable between compilations, so a ModuleId visible to
@ -139,10 +140,12 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
})
.collect();
//Create a list
let exposed: HashMap<_, _> = exposes
.into_iter()
.map(|(id, symbols)| (id, Arc::new(symbols)))
.collect();
let exposed = Arc::new(Mutex::new(
exposes
.into_iter()
.map(|(id, symbols)| (id, Arc::new(symbols)))
.collect::<HashMap<_, _>>(),
));
//Combine the subs from all modules
let all_subs = Arc::new(Mutex::new(
typechecked
@ -240,7 +243,7 @@ struct AnalyzedDocumentBuilder<'a> {
root_module: &'a mut Option<RootModule>,
imports: &'a mut MutMap<ModuleId, MutSet<ModuleId>>,
exposed_imports: HashMap<ModuleId, Vec<(Symbol, Variable)>>,
exposed: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
exposed: Arc<Mutex<HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>>>,
all_subs: Arc<Mutex<HashMap<ModuleId, Subs>>>,
}
@ -266,7 +269,11 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
.map(|id| {
(
id,
self.exposed.get(&id).unwrap_or(&Arc::new(vec![])).clone(),
self.exposed
.lock()
.get(&id)
.unwrap_or(&Arc::new(vec![]))
.clone(),
)
})
.collect::<HashMap<_, _>>();
@ -296,6 +303,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
other_modules_subs: self.all_subs.clone(),
interns: self.interns.clone(),
module_id_to_url: self.module_id_to_url.clone(),
modules_exposed: self.exposed.clone(),
};
let line_info = LineInfo::new(&source);

View File

@ -226,9 +226,10 @@ impl AnalyzedDocument {
subs,
declarations,
exposed_imports,
aliases,
imports,
other_modules_subs,
modules_exposed,
..
} = self.module()?;
@ -250,6 +251,7 @@ impl AnalyzedDocument {
interns,
imports,
other_modules_subs,
modules_exposed,
true,
))
} else {
@ -276,6 +278,7 @@ impl AnalyzedDocument {
interns,
imports,
other_modules_subs,
modules_exposed,
true,
);
Some(completions)

View File

@ -5,6 +5,7 @@ use parking_lot::Mutex;
use roc_can::{
def::Def,
expr::{ClosureData, Declarations, Expr, WhenBranch},
module::ExposedByModule,
pattern::{ListPatterns, Pattern, RecordDestruct, TupleDestruct},
traverse::{walk_decl, walk_def, walk_expr, DeclarationInfo, Visitor},
};
@ -15,9 +16,12 @@ use roc_types::{
subs::{Subs, Variable},
types::Alias,
};
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind};
use tower_lsp::lsp_types::{
CompletionItem, CompletionItemKind, Documentation, MarkupContent, MarkupKind,
};
use super::utils::format_var_type;
mod formatting;
pub struct CompletionVisitor<'a> {
position: Position,
@ -294,7 +298,6 @@ fn make_completion_item(
label: str,
detail: Some(type_str),
kind: Some(typ),
..Default::default()
}
}
@ -327,37 +330,47 @@ pub fn get_upper_case_completion_items(
interns: &Interns,
imported_modules: &HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
all_subs: &Mutex<HashMap<ModuleId, Subs>>,
modules_exposed: &Mutex<HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>>,
just_modules: bool,
) -> Vec<CompletionItem> {
//TODO! use a proper completion type instead of simple
let module_completions = imported_modules.iter().flat_map(|(mod_id, vars)| {
let mod_name = mod_id.to_ident_str(interns).to_string();
if mod_name.starts_with(&prefix) {
let item = CompletionItem {
label: mod_name.clone(),
kind: Some(CompletionItemKind::MODULE),
detail: Some(format!("`{0}` module", mod_name)),
documentation: Some(formatting::module_documentation(
formatting::DescripitonType::Exposes,
mod_id,
&mod_name,
interns,
imported_modules,
all_subs,
modules_exposed,
)),
..Default::default()
};
vec![item]
} else if prefix.starts_with(&mod_name) {
//Complete dot completions
} else if prefix.starts_with(&(mod_name + ".")) {
vars.clone()
.iter()
.map(|(sym, var)| {
//TODO! I need to get subs from the module we are completing from
let detail = all_subs
all_subs
.lock()
.get_mut(mod_id)
.map(|subs| format_var_type(*var, subs, module_id, interns))
.unwrap();
CompletionItem {
label: sym.as_str(interns).to_string(),
kind: Some(CompletionItemKind::MODULE),
detail: Some(detail),
..Default::default()
}
.map(|subs| {
make_completion_item(
subs,
mod_id,
interns,
sym.as_str(interns).to_string(),
*var,
)
})
.expect("Couldn't find subs for module during completion.")
})
.collect::<Vec<_>>()
} else {

View File

@ -0,0 +1,70 @@
use std::{collections::HashMap, sync::Arc};
use parking_lot::Mutex;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_types::subs::{Subs, Variable};
use tower_lsp::lsp_types::{Documentation, MarkupContent, MarkupKind};
use crate::analysis::utils::format_var_type;
fn module_exposed_list(
module_id: &ModuleId,
interns: &Interns,
imported_modules: &HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
all_subs: &Mutex<HashMap<ModuleId, Subs>>,
modules_exposed: &Mutex<HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>>,
) -> Option<std::string::String> {
modules_exposed.lock().get(module_id).and_then(|exposed| {
all_subs.lock().get_mut(module_id).map(|subs| {
let items = exposed
.iter()
.map(|(symb, var)| {
let var_str = format_var_type(*var, subs, module_id, interns);
format!(" {0}: {1}", symb.as_str(interns), var_str)
})
.collect::<Vec<_>>();
format!("{{\n{0}\n}}", items.join(",\n"))
})
})
}
pub enum DescripitonType {
Name,
Exposes,
NameAndExposes,
}
fn md_doc(val: String) -> Documentation {
Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: val,
})
}
pub fn module_documentation(
description_type: DescripitonType,
module_id: &ModuleId,
mod_name: &String,
interns: &Interns,
imported_modules: &HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
all_subs: &Mutex<HashMap<ModuleId, Subs>>,
modules_exposed: &Mutex<HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>>,
) -> Documentation {
let exposed = || {
module_exposed_list(
module_id,
interns,
imported_modules,
all_subs,
modules_exposed,
)
.unwrap_or_default()
};
match description_type {
DescripitonType::Name => md_doc(format!("{0} module", mod_name)),
DescripitonType::Exposes => md_doc(format!("```roc\n{0}\n```", exposed())),
DescripitonType::NameAndExposes => {
md_doc(format!("{0}\n```roc\n{1}\n```", mod_name, exposed()))
}
}
}