working completions

This commit is contained in:
Eli Dowling 2024-02-11 10:48:02 +10:00 committed by Eli Dowling
parent f5e32f7e16
commit e809beb882
6 changed files with 238 additions and 29 deletions

View file

@ -2537,6 +2537,14 @@ fn update<'a>(
} }
report_unused_imported_modules(&mut state, module_id, &constrained_module); report_unused_imported_modules(&mut state, module_id, &constrained_module);
state
.module_cache
.imported_modules
.insert(module_id, constrained_module.imported_modules.clone());
state
.module_cache
.exposed_imports
.insert(module_id, constrained_module.module.exposed_imports.clone());
state state
.module_cache .module_cache
@ -2583,6 +2591,10 @@ fn update<'a>(
.module_cache .module_cache
.type_problems .type_problems
.insert(module_id, solved_module.problems); .insert(module_id, solved_module.problems);
state
.module_cache
.exposes
.insert(module_id, solved_module.exposed_vars_by_symbol.clone());
let should_include_expects = (!loc_expects.is_empty() || !loc_dbgs.is_empty()) && { let should_include_expects = (!loc_expects.is_empty() || !loc_dbgs.is_empty()) && {
let modules = state.arc_modules.lock(); let modules = state.arc_modules.lock();
@ -2737,6 +2749,7 @@ fn update<'a>(
state.module_cache.checked.insert( state.module_cache.checked.insert(
module_id, module_id,
CheckedModule { CheckedModule {
aliases: solved_module.aliases,
solved_subs, solved_subs,
decls, decls,
abilities_store, abilities_store,
@ -3400,6 +3413,10 @@ fn finish(
timings: state.timings, timings: state.timings,
docs_by_module, docs_by_module,
abilities_store, abilities_store,
imported_modules: state.module_cache.imported_modules,
exposed_imports: state.module_cache.exposed_imports,
imports: state.module_cache.imports,
exposes: state.module_cache.exposes,
} }
} }

View file

@ -46,6 +46,11 @@ pub struct LoadedModule {
pub docs_by_module: Vec<(ModuleId, ModuleDocumentation)>, pub docs_by_module: Vec<(ModuleId, ModuleDocumentation)>,
pub abilities_store: AbilitiesStore, pub abilities_store: AbilitiesStore,
pub typechecked: MutMap<ModuleId, CheckedModule>, pub typechecked: MutMap<ModuleId, CheckedModule>,
pub imports: MutMap<ModuleId, MutSet<ModuleId>>,
pub imported_modules: MutMap<ModuleId, MutMap<ModuleId, Region>>,
pub exposed_imports: MutMap<ModuleId, MutMap<Symbol, Region>>,
pub exposes: MutMap<ModuleId, Vec<(Symbol, Variable)>>,
} }
impl LoadedModule { impl LoadedModule {
@ -132,6 +137,10 @@ pub struct TypeCheckedModule<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct CheckedModule { pub struct CheckedModule {
/// all aliases and their definitions. this has to include non-exposed aliases
/// because exposed aliases can depend on non-exposed ones)
pub aliases: MutMap<Symbol, (bool, Alias)>,
pub solved_subs: Solved<Subs>, pub solved_subs: Solved<Subs>,
pub decls: Declarations, pub decls: Declarations,
pub abilities_store: AbilitiesStore, pub abilities_store: AbilitiesStore,

View file

@ -9,7 +9,9 @@ use roc_module::ident::ModuleName;
use roc_module::symbol::{ModuleId, PQModuleName, Symbol}; use roc_module::symbol::{ModuleId, PQModuleName, Symbol};
use roc_mono::ir::ExternalSpecializations; use roc_mono::ir::ExternalSpecializations;
use roc_problem::Severity; use roc_problem::Severity;
use roc_region::all::Region;
use roc_solve_problem::TypeError; use roc_solve_problem::TypeError;
use roc_types::subs::Variable;
use roc_types::types::Alias; use roc_types::types::Alias;
use std::path::PathBuf; use std::path::PathBuf;
@ -33,6 +35,9 @@ pub(crate) struct ModuleCache<'a> {
/// Various information /// Various information
pub(crate) imports: MutMap<ModuleId, MutSet<ModuleId>>, pub(crate) imports: MutMap<ModuleId, MutSet<ModuleId>>,
pub(crate) exposes: MutMap<ModuleId, Vec<(Symbol, Variable)>>,
pub(crate) imported_modules: MutMap<ModuleId, MutMap<ModuleId, Region>>,
pub(crate) exposed_imports: MutMap<ModuleId, MutMap<Symbol, Region>>,
pub(crate) top_level_thunks: MutMap<ModuleId, MutSet<Symbol>>, pub(crate) top_level_thunks: MutMap<ModuleId, MutSet<Symbol>>,
pub(crate) documentation: VecMap<ModuleId, ModuleDocumentation>, pub(crate) documentation: VecMap<ModuleId, ModuleDocumentation>,
pub(crate) can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>, pub(crate) can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
@ -103,6 +108,9 @@ impl Default for ModuleCache<'_> {
late_specializations: Default::default(), late_specializations: Default::default(),
external_specializations_requested: Default::default(), external_specializations_requested: Default::default(),
imports: Default::default(), imports: Default::default(),
imported_modules: Default::default(),
exposed_imports: Default::default(),
exposes: Default::default(),
top_level_thunks: Default::default(), top_level_thunks: Default::default(),
documentation: Default::default(), documentation: Default::default(),
can_problems: Default::default(), can_problems: Default::default(),

View file

@ -1,15 +1,23 @@
use std::path::{Path, PathBuf}; use std::{
collections::{HashMap, HashSet},
path::{Path, PathBuf},
sync::Arc,
};
use bumpalo::Bump; use bumpalo::Bump;
use log::debug;
use roc_can::{abilities::AbilitiesStore, expr::Declarations}; use roc_can::{abilities::AbilitiesStore, expr::Declarations};
use roc_collections::MutMap; use roc_collections::{MutMap, MutSet};
use roc_load::{CheckedModule, LoadedModule}; use roc_load::{CheckedModule, LoadedModule};
use roc_module::symbol::{Interns, ModuleId}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_packaging::cache::{self, RocCacheDir}; use roc_packaging::cache::{self, RocCacheDir};
use roc_region::all::LineInfo; use roc_region::all::{LineInfo, Region};
use roc_reporting::report::RocDocAllocator; use roc_reporting::report::RocDocAllocator;
use roc_solve_problem::TypeError; use roc_solve_problem::TypeError;
use roc_types::subs::Subs; use roc_types::{
subs::{Subs, Variable},
types::Alias,
};
use tower_lsp::lsp_types::{Diagnostic, SemanticTokenType, Url}; use tower_lsp::lsp_types::{Diagnostic, SemanticTokenType, Url};
@ -29,6 +37,9 @@ pub const HIGHLIGHT_TOKENS_LEGEND: &[SemanticTokenType] = Token::LEGEND;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(super) struct AnalyzedModule { pub(super) struct AnalyzedModule {
exposed_imports: Vec<(Symbol, Variable)>,
imports: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
aliases: MutMap<Symbol, (bool, Alias)>,
module_id: ModuleId, module_id: ModuleId,
interns: Interns, interns: Interns,
subs: Subs, subs: Subs,
@ -92,6 +103,12 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
mut typechecked, mut typechecked,
solved, solved,
abilities_store, abilities_store,
docs_by_module,
imported_modules,
mut exposed_imports,
mut imports,
exposed_types_storage,
mut exposes,
.. ..
} = module; } = module;
@ -99,7 +116,25 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
subs: solved.into_inner(), subs: solved.into_inner(),
abilities_store, abilities_store,
}); });
let exposed_imports: HashMap<_, _> = exposed_imports
.into_iter()
.map(|(id, symbols)| {
(
id,
symbols
.into_iter()
.filter_map(|(symbol, _)| {
exposes.get(&id)?.iter().find(|(symb, _)| symb == &symbol)
})
.cloned()
.collect::<Vec<_>>(),
)
})
.collect();
let exposed: HashMap<_, _> = exposes
.into_iter()
.map(|(id, symbols)| (id, Arc::new(symbols)))
.collect();
let mut builder = AnalyzedDocumentBuilder { let mut builder = AnalyzedDocumentBuilder {
interns: &interns, interns: &interns,
module_id_to_url: module_id_to_url_from_sources(&sources), module_id_to_url: module_id_to_url_from_sources(&sources),
@ -108,10 +143,32 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
declarations_by_id: &mut declarations_by_id, declarations_by_id: &mut declarations_by_id,
typechecked: &mut typechecked, typechecked: &mut typechecked,
root_module: &mut root_module, root_module: &mut root_module,
exposed_imports,
imports: &mut imports,
exposed,
}; };
debug!("docs: {:#?}", docs_by_module);
for (module_id, (path, source)) in sources { for (module_id, (path, source)) in sources {
documents.push(builder.build_document(path, source, module_id, doc_info.version)); let doc = builder.build_document(path, source, module_id, doc_info.version);
debug!("================");
debug!("module:{:?}", module_id);
// let exposed_imp = exposed_imports.get(&module_id)?;
if let Some(modu) = &doc.analysis_result.module {
let aliases = &modu.aliases;
let imports = &modu.imports;
// debug!("interns modules:{:#?}", module.interns.module_ids);
// debug!("exposed_imports:{:#?}", exposed_imp);
// debug!("exposed:{:#?}", );
debug!("imports:{:#?}", imports);
// debug!("docs:{:#?}", docs.1.entries);
debug!("alais:{:#?}", aliases);
// debug!("decls:{:#?}", module.declarations)
}
documents.push(doc);
} }
documents documents
@ -165,6 +222,9 @@ struct AnalyzedDocumentBuilder<'a> {
declarations_by_id: &'a mut MutMap<ModuleId, Declarations>, declarations_by_id: &'a mut MutMap<ModuleId, Declarations>,
typechecked: &'a mut MutMap<ModuleId, CheckedModule>, typechecked: &'a mut MutMap<ModuleId, CheckedModule>,
root_module: &'a mut Option<RootModule>, 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)>>>,
} }
impl<'a> AnalyzedDocumentBuilder<'a> { impl<'a> AnalyzedDocumentBuilder<'a> {
@ -178,19 +238,38 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
let subs; let subs;
let abilities; let abilities;
let declarations; let declarations;
let aliases;
let imports = self
.imports
.remove(&module_id)
.unwrap_or_default()
.into_iter()
.map(|id| {
(
id,
self.exposed.get(&id).unwrap_or(&Arc::new(vec![])).clone(),
)
})
.collect::<HashMap<_, _>>();
let exposed_imports = self.exposed_imports.remove(&module_id).unwrap_or_default();
if let Some(m) = self.typechecked.remove(&module_id) { if let Some(m) = self.typechecked.remove(&module_id) {
subs = m.solved_subs.into_inner(); subs = m.solved_subs.into_inner();
abilities = m.abilities_store; abilities = m.abilities_store;
declarations = m.decls; declarations = m.decls;
aliases = m.aliases;
} else { } else {
let rm = self.root_module.take().unwrap(); let rm = self.root_module.take().unwrap();
subs = rm.subs; subs = rm.subs;
abilities = rm.abilities_store; abilities = rm.abilities_store;
declarations = self.declarations_by_id.remove(&module_id).unwrap(); declarations = self.declarations_by_id.remove(&module_id).unwrap();
aliases = HashMap::default();
} }
let analyzed_module = AnalyzedModule { let analyzed_module = AnalyzedModule {
exposed_imports,
imports,
aliases,
subs, subs,
abilities, abilities,
declarations, declarations,

View file

@ -13,7 +13,9 @@ use tower_lsp::lsp_types::{
}; };
use crate::{ use crate::{
analysis::completion::{field_completion, get_completion_items}, analysis::completion::{
field_completion, get_completion_items, get_upper_case_completion_items,
},
convert::{ToRange, ToRocPosition}, convert::{ToRange, ToRocPosition},
}; };
@ -223,29 +225,67 @@ impl AnalyzedDocument {
interns, interns,
subs, subs,
declarations, declarations,
exposed_imports,
aliases,
imports,
.. ..
} = self.module()?; } = self.module()?;
let is_field_completion = symbol_prefix.contains('.'); let is_field_or_module_completion = symbol_prefix.contains('.');
if is_field_completion {
field_completion( if is_field_or_module_completion {
position, //if the second last second is capitalised we know we are completing a module of an import of a module
symbol_prefix, let is_module_completion = symbol_prefix
declarations, .split('.')
interns, .nth_back(1)
&mut subs.clone(), .map(|a| a.chars().nth(0).unwrap().is_uppercase())
module_id, .unwrap_or(false);
) if is_module_completion {
Some(get_upper_case_completion_items(
position,
symbol_prefix,
module_id,
interns,
&mut subs.clone(),
imports,
aliases,
true,
))
} else {
field_completion(
position,
symbol_prefix,
&declarations,
&interns,
&mut subs.clone(),
&module_id,
)
}
} else { } else {
let completions = get_completion_items( let is_module_or_type_completion = symbol_prefix.chars().nth(0).unwrap().is_uppercase();
position, if is_module_or_type_completion {
symbol_prefix, let completions = get_upper_case_completion_items(
declarations, position,
&mut subs.clone(), symbol_prefix,
module_id, module_id,
interns, interns,
); &mut subs.clone(),
Some(completions) imports,
aliases,
false,
);
Some(completions)
} else {
let completions = get_completion_items(
position,
symbol_prefix,
declarations,
&mut subs.clone(),
module_id,
interns,
);
Some(completions)
}
} }
} }
} }

View file

@ -1,3 +1,8 @@
use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use log::{debug, trace, warn}; use log::{debug, trace, warn};
use roc_can::{ use roc_can::{
def::Def, def::Def,
@ -5,9 +10,13 @@ use roc_can::{
pattern::{ListPatterns, Pattern, RecordDestruct, TupleDestruct}, pattern::{ListPatterns, Pattern, RecordDestruct, TupleDestruct},
traverse::{walk_decl, walk_def, walk_expr, DeclarationInfo, Visitor}, traverse::{walk_decl, walk_def, walk_expr, DeclarationInfo, Visitor},
}; };
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_collections::{MutMap, MutSet};
use roc_module::symbol::{self, Interns, ModuleId, Symbol};
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position, Region};
use roc_types::subs::{Subs, Variable}; use roc_types::{
subs::{Subs, Variable},
types::Alias,
};
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind}; use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind};
use super::utils::format_var_type; use super::utils::format_var_type;
@ -311,6 +320,53 @@ pub fn get_completion_items(
.collect(), .collect(),
) )
} }
pub fn get_upper_case_completion_items(
position: Position,
prefix: String,
module_id: &ModuleId,
interns: &Interns,
subs: &mut Subs,
imported_modules: &HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
aliases: &MutMap<Symbol, (bool, Alias)>,
just_modules: bool,
) -> Vec<CompletionItem> {
//TODO! use a proper completion type instead of simple
let module_completions = imported_modules.into_iter().flat_map(|(mod_id, vars)| {
let mod_name = mod_id.to_ident_str(interns).to_string();
if mod_name.starts_with(&prefix) {
vec![CompletionItem::new_simple(
mod_name.clone(),
format!("`{0}` module", mod_name),
)]
} else if prefix.starts_with(&mod_name) {
make_completion_items(
subs,
module_id,
interns,
vars.clone()
.iter()
.map(|(sym, vars)| (sym.as_str(interns).to_string(), vars.clone()))
.collect::<Vec<_>>(),
)
} else {
vec![]
}
});
if (just_modules) {
return module_completions.collect();
}
//TODO! use a proper completion type instead of simple
let aliases_completions = aliases
.iter()
.filter(|(symbol, (exposed, alias))| &symbol.module_id() == module_id)
.map(|(symbol, (exposed, alias))| {
let name = symbol.as_str(interns).to_string();
CompletionItem::new_simple(name.clone(), name + "we don't know how to print types ")
});
module_completions.chain(aliases_completions).collect()
}
fn make_completion_items( fn make_completion_items(
subs: &mut Subs, subs: &mut Subs,
module_id: &ModuleId, module_id: &ModuleId,