stylistic improvements

This commit is contained in:
Anton-4 2024-03-13 20:27:23 +01:00
parent 2c5b085ed5
commit 1efd7615f0
No known key found for this signature in database
GPG key ID: 0971D718C0A9B937
6 changed files with 90 additions and 45 deletions

View file

@ -41,8 +41,12 @@ impl<'a> Formattable for Pattern<'a> {
fn is_multiline(&self) -> bool {
// Theory: a pattern should only be multiline when it contains a comment
match self {
Pattern::SpaceBefore(a, spaces) | Pattern::SpaceAfter(a, spaces) => {
debug_assert!(!spaces.is_empty(), "spaces is empty in pattern {:#?}", a);
Pattern::SpaceBefore(pattern, spaces) | Pattern::SpaceAfter(pattern, spaces) => {
debug_assert!(
!spaces.is_empty(),
"spaces is empty in pattern {:#?}",
pattern
);
spaces.iter().any(|s| s.is_comment())
}

View file

@ -33,7 +33,7 @@ use self::{analysed_doc::ModuleIdToUrl, tokens::Token};
pub const HIGHLIGHT_TOKENS_LEGEND: &[SemanticTokenType] = Token::LEGEND;
///Contains maps of info about all modules that were analyzed
/// Contains hashmaps of info about all modules that were analyzed
#[derive(Debug)]
pub(super) struct ModulesInfo {
subs: Mutex<HashMap<ModuleId, Subs>>,
@ -41,30 +41,38 @@ pub(super) struct ModulesInfo {
}
impl ModulesInfo {
/// Apply function to subs
fn with_subs<F, A>(&self, mod_id: &ModuleId, f: F) -> Option<A>
where
F: FnOnce(&mut Subs) -> A,
{
self.subs.lock().get_mut(mod_id).map(f)
}
///Transforms some of the raw data from the analysis into a state that is more useful during processes like completion
/// Transforms some of the raw data from the analysis into a state that is
/// more useful during processes like completion.
fn from_analysis(
exposes: MutMap<ModuleId, Vec<(Symbol, Variable)>>,
typechecked: &MutMap<ModuleId, CheckedModule>,
) -> ModulesInfo {
//We wrap this in arc because later we will go through each module's imports and store the full list of symbols that each imported module exposes.
//eg: A imports B. B exposes [add, multiply, divide] and A will store a reference to that list.
// We wrap this in Arc because later we will go through each module's imports and
// store the full list of symbols that each imported module exposes.
// example: A imports B. B exposes [add, multiply, divide] and A will store a reference to that list.
let exposed = exposes
.into_iter()
.map(|(id, symbols)| (id, Arc::new(symbols)))
.map(|(module_id, symbols)| (module_id, Arc::new(symbols)))
.collect::<HashMap<_, _>>();
//Combine the subs from all modules
// Combine the subs from all modules
let all_subs = Mutex::new(
typechecked
.iter()
.map(|(k, v)| (*k, v.solved_subs.0.clone()))
.map(|(module_id, checked_module)| {
(*module_id, checked_module.solved_subs.0.clone())
})
.collect::<HashMap<_, _>>(),
);
ModulesInfo {
subs: all_subs,
exposed,
@ -75,7 +83,7 @@ impl ModulesInfo {
#[derive(Debug, Clone)]
pub(super) struct AnalyzedModule {
exposed_imports: Vec<(Symbol, Variable)>,
///This modules imports grouped by which module they come from
/// imports are grouped by which module they come from
imports: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
module_id: ModuleId,
interns: Interns,
@ -141,8 +149,8 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
mut typechecked,
solved,
abilities_store,
exposed_imports,
mut imports,
exposed_imports,
exposes,
..
} = module;
@ -155,6 +163,7 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
let exposed_imports = resolve_exposed_imports(exposed_imports, &exposes);
let modules_info = Arc::new(ModulesInfo::from_analysis(exposes, &typechecked));
let mut builder = AnalyzedDocumentBuilder {
interns: &interns,
module_id_to_url: module_id_to_url_from_sources(&sources),
@ -176,8 +185,9 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
documents
}
///Take the exposed imports from each module, lookup the symbol within that module's list of exposed symbols and then get the type info for that import
///eg: `import {Task.{await}}`. `await` is an exposed_import, so we need to lookup its type info
/// Take the exposed imports from each module, lookup the symbol within that module's list of
/// exposed symbols and then get the type info for that import.
/// example: `import {Task.{await}}`. `await` is an exposed_import, so we need to lookup its type info.
fn resolve_exposed_imports(
exposed_imports: MutMap<ModuleId, MutMap<Symbol, roc_region::all::Region>>,
exposes: &MutMap<ModuleId, Vec<(Symbol, Variable)>>,

View file

@ -232,14 +232,16 @@ impl AnalyzedDocument {
let is_field_or_module_completion = symbol_prefix.contains('.');
if is_field_or_module_completion {
//if the second last section is capitalised we know we are completing a module of an import of a module eg: My.Module.function
// If the second to last section is capitalised we know we are completing a
// module inside an import of a module, e.g.: My.Module.function
let is_module_completion = symbol_prefix
.split('.')
.nth_back(1)
.and_then(|a| a.chars().nth(0).map(|c| c.is_uppercase()))
.nth_back(1) // second to last
.and_then(|str| str.chars().nth(0).map(|c| c.is_uppercase()))
.unwrap_or(false);
if is_module_completion {
info!("Getting module dot completion");
info!("Getting module dot completion...");
Some(get_module_completion_items(
symbol_prefix,
interns,
@ -248,7 +250,7 @@ impl AnalyzedDocument {
true,
))
} else {
info!("Getting record dot completion");
info!("Getting record dot completion...");
field_completion(
position,
symbol_prefix,
@ -263,8 +265,9 @@ impl AnalyzedDocument {
.chars()
.nth(0)
.map_or(false, |c| c.is_uppercase());
if is_module_or_type_completion {
info!("Getting module completion");
info!("Getting module completion...");
let completions = get_module_completion_items(
symbol_prefix,
interns,
@ -274,7 +277,7 @@ impl AnalyzedDocument {
);
Some(completions)
} else {
info!("Getting variable completion");
info!("Getting variable completion...");
let completions = get_completion_items(
position,
symbol_prefix,

View file

@ -298,7 +298,10 @@ fn make_completion_item(
..Default::default()
}
}
/// Walks through declarations that would be accessible from the provided position adding them to a list of completion items until all accessible declarations have been fully explored
/// Walks through declarations that would be accessible from the provided
/// position, adding them to a list of completion items until all accessible
/// declarations have been fully explored.
pub fn get_completion_items(
position: Position,
prefix: String,
@ -310,7 +313,9 @@ pub fn get_completion_items(
) -> Vec<CompletionItem> {
let mut completions = get_completions(position, decls, prefix, interns);
completions.extend(exposed_imports);
debug!("extended with:{:#?}", exposed_imports);
make_completion_items(
subs,
module_id,
@ -321,6 +326,7 @@ pub fn get_completion_items(
.collect(),
)
}
pub(super) fn get_module_completion_items(
prefix: String,
interns: &Interns,
@ -347,16 +353,19 @@ pub(super) fn get_module_completion_items(
..Default::default()
};
vec![item]
//Complete dot completions
// Complete dot completions
} else if prefix.starts_with(&(mod_name + ".")) {
get_module_exposed_completion(exposed_symbols, modules_info, mod_id, interns)
} else {
vec![]
}
});
if just_modules {
return module_completions.collect();
}
module_completions.collect()
}
@ -369,7 +378,8 @@ fn get_module_exposed_completion(
exposed_symbols
.iter()
.map(|(sym, var)| {
//We need to fetch the subs for the module that is exposing what we are trying to complete because that will have the type info we need
// We need to fetch the subs for the module that is exposing what we
// are trying to complete, because that will have the type info we need.
modules_info
.with_subs(mod_id, |subs| {
make_completion_item(
@ -385,8 +395,8 @@ fn get_module_exposed_completion(
.collect::<Vec<_>>()
}
///Provides a list of completions for Type aliases within the scope.
///TODO: Use this when we know we are within a type definition
/// Provides a list of completions for Type aliases within the scope.
/// TODO: Use this when we know we are within a type definition.
fn _alias_completions(
aliases: &MutMap<Symbol, (bool, Alias)>,
module_id: &ModuleId,
@ -397,9 +407,10 @@ fn _alias_completions(
.filter(|(symbol, (_exposed, _alias))| &symbol.module_id() == module_id)
.map(|(symbol, (_exposed, _alias))| {
let name = symbol.as_str(interns).to_string();
CompletionItem {
label: name.clone(),
detail: Some(name + "we don't know how to print types "),
detail: Some(name + " we don't know how to print types."),
kind: Some(CompletionItemKind::CLASS),
..Default::default()
}

View file

@ -5,7 +5,7 @@ use tower_lsp::lsp_types::{Documentation, MarkupContent, MarkupKind};
use crate::analysis::{utils::format_var_type, ModulesInfo};
fn module_exposed_list(
fn get_module_exposed_list(
module_id: &ModuleId,
interns: &Interns,
modules_info: &ModulesInfo,
@ -14,9 +14,9 @@ fn module_exposed_list(
modules_info.with_subs(module_id, |subs| {
let items = exposed
.iter()
.map(|(symb, var)| {
.map(|(symbol, var)| {
let var_str = format_var_type(*var, subs, module_id, interns);
format!("{0}: {1}", symb.as_str(interns), var_str)
format!("{0}: {1}", symbol.as_str(interns), var_str)
})
.collect::<Vec<_>>();
@ -26,14 +26,15 @@ fn module_exposed_list(
pub(super) enum DescriptionsType {
Exposes,
}
fn md_doc(val: String) -> Documentation {
Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: val,
})
}
///Generates a nicely formatted block of text for the completionitem documentation field
/// Generates a nicely formatted block of text for the completionitem documentation field.
pub(super) fn module_documentation(
description_type: DescriptionsType,
module_id: &ModuleId,
@ -42,7 +43,7 @@ pub(super) fn module_documentation(
modules_info: &ModulesInfo,
) -> Documentation {
let exposed_string =
module_exposed_list(module_id, interns, modules_info, exposed).unwrap_or_default();
get_module_exposed_list(module_id, interns, modules_info, exposed).unwrap_or_default();
match description_type {
DescriptionsType::Exposes => md_doc(format!("```roc\n{0}\n```", exposed_string)),

View file

@ -254,6 +254,7 @@ impl LanguageServer for RocServer {
},
work_done_progress_params: _,
} = params;
unwind_async(self.state.registry.hover(&text_document.uri, position)).await
}
@ -270,6 +271,7 @@ impl LanguageServer for RocServer {
work_done_progress_params: _,
partial_result_params: _,
} = params;
unwind_async(
self.state
.registry
@ -284,6 +286,7 @@ impl LanguageServer for RocServer {
options: _,
work_done_progress_params: _,
} = params;
unwind_async(self.state.registry.formatting(&text_document.uri)).await
}
@ -296,12 +299,14 @@ impl LanguageServer for RocServer {
work_done_progress_params: _,
partial_result_params: _,
} = params;
unwind_async(self.state.registry.semantic_tokens(&text_document.uri)).await
}
async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
let doc = params.text_document_position;
trace!("Got completion request");
trace!("Got completion request.");
unwind_async(
self.state
.registry
@ -310,13 +315,16 @@ impl LanguageServer for RocServer {
.await
}
}
async fn unwind_async<F, T>(f: F) -> tower_lsp::jsonrpc::Result<T>
async fn unwind_async<Fut, T>(future: Fut) -> tower_lsp::jsonrpc::Result<T>
where
F: Future<Output = T>,
Fut: Future<Output = T>,
{
let result = { futures::FutureExt::catch_unwind(AssertUnwindSafe(f)).await };
let result = { futures::FutureExt::catch_unwind(AssertUnwindSafe(future)).await };
match result {
Ok(a) => tower_lsp::jsonrpc::Result::Ok(a),
Err(err) => tower_lsp::jsonrpc::Result::Err(jsonrpc::Error {
code: jsonrpc::ErrorCode::InternalError,
message: format!("{:?}", err),
@ -354,7 +362,8 @@ mod tests {
.map(|item| item.label)
.collect::<Vec<_>>()
}
///Gets completion and returns only the label for each completion
/// Gets completion and returns only the label for each completion
async fn get_completion_labels(
reg: &Registry,
url: &Url,
@ -370,7 +379,9 @@ mod tests {
exposes []
imports []
"#};
static INIT: Once = Once::new();
async fn test_setup(doc: String) -> (RocServerState, Url) {
INIT.call_once(|| {
env_logger::builder()
@ -382,11 +393,12 @@ mod tests {
let url = Url::parse("file:/Test.roc").unwrap();
let inner = RocServerState::new(RocServerConfig::default(), Registry::default());
//setup the file
// setup the file
inner.change(&url, doc, 0).await.unwrap();
(inner, url)
}
///Runs a basic completion and returns the response
/// Runs a basic completion and returns the response
async fn completion_test(
initial: &str,
addition: &str,
@ -394,17 +406,17 @@ mod tests {
) -> Option<std::vec::Vec<std::string::String>> {
let doc = DOC_LIT.to_string() + initial;
let (inner, url) = test_setup(doc.clone()).await;
let reg = &inner.registry;
let registry = &inner.registry;
let change = doc.clone() + addition;
info!("doc is:\n{0}", change);
inner.change(&url, change, 1).await.unwrap();
get_completion_labels(reg, &url, position).await
get_completion_labels(registry, &url, position).await
}
///Test that completion works properly when we apply an "as" pattern to an identifier
/// Tests that completion works properly when we apply an "as" pattern to an identifier.
#[tokio::test]
async fn test_completion_as_identifier() {
let suffix = DOC_LIT.to_string()
@ -413,6 +425,7 @@ mod tests {
when a is
inn as outer ->
"#};
let (inner, url) = test_setup(suffix.clone()).await;
let position = Position::new(6, 7);
let reg = &inner.registry;
@ -426,6 +439,7 @@ mod tests {
let comp2 = get_completion_labels(reg, &url, position).await;
let actual = [comp1, comp2];
expect![[r#"
[
Some(
@ -444,7 +458,7 @@ mod tests {
.assert_debug_eq(&actual)
}
///Test that completion works properly when we apply an "as" pattern to a record
/// Tests that completion works properly when we apply an "as" pattern to a record.
#[tokio::test]
async fn test_completion_as_record() {
let doc = DOC_LIT.to_string()
@ -487,7 +501,8 @@ mod tests {
"#]]
.assert_debug_eq(&actual);
}
///Test that completion works properly when we apply an "as" pattern to a record
/// Tests that completion of function args in scope works properly.
#[tokio::test]
async fn test_completion_fun_params() {
let actual = completion_test(
@ -509,11 +524,12 @@ mod tests {
"#]]
.assert_debug_eq(&actual);
}
#[tokio::test]
async fn test_completion_closure() {
async fn test_completion_fun_params_map() {
let actual = completion_test(
indoc! {r"
main = [] |> List.map \ param1 , param2->
main = [] |> List.map \param1 , param2->
"},
"par",
Position::new(4, 3),