mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-10 02:59:34 +00:00
stylistic improvements
This commit is contained in:
parent
2c5b085ed5
commit
1efd7615f0
6 changed files with 90 additions and 45 deletions
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)>>,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue