Merge branch 'main' into inline-imports

This commit is contained in:
Agus Zubiaga 2024-04-22 17:45:25 -03:00
commit 4476277a56
No known key found for this signature in database
28 changed files with 142 additions and 84 deletions

2
Cargo.lock generated
View file

@ -2665,7 +2665,7 @@ name = "roc_ident"
version = "0.0.1"
[[package]]
name = "roc_lang_srv"
name = "roc_language_server"
version = "0.0.1"
dependencies = [
"bumpalo",

View file

@ -27,7 +27,7 @@ members = [
"crates/wasi-libc-sys",
"crates/wasm_module",
"crates/wasm_interp",
"crates/lang_srv",
"crates/language_server",
]
exclude = [
@ -195,4 +195,4 @@ lto = "thin" # TODO: We could consider full here since this is only used for pac
[profile.debug-full]
inherits = "dev"
debug = true
debug = true

View file

@ -33,6 +33,8 @@ If you would like your company to become a corporate sponsor of Roc's developmen
We'd also like to express our gratitude to our generous [individual sponsors](https://github.com/sponsors/roc-lang/)! A special thanks to those sponsoring $25/month or more:
* [Angelo Ceccato](https://github.com/AngeloChecked)
* [Niclas Overby](https://github.com/noverby)
* [Krzysztof G.](https://github.com/krzysztofgb)
* [Sam Mohr](https://github.com/smores56)
* [Steven Chen](https://github.com/megakilo)

View file

@ -197,17 +197,12 @@ where
)
}
pub fn check_indent<'a, E>(
indent_problem: fn(Position) -> E,
inside_suffixed_statement: bool,
) -> impl Parser<'a, (), E>
pub fn check_indent<'a, E>(indent_problem: fn(Position) -> E) -> impl Parser<'a, (), E>
where
E: 'a,
{
let extra_spaces = if inside_suffixed_statement { 1 } else { 0 };
move |_, state: State<'a>, min_indent: u32| {
if state.column() >= (min_indent + extra_spaces) {
if state.column() >= min_indent {
Ok((NoProgress, (), state))
} else {
Err((NoProgress, indent_problem(state.pos())))

View file

@ -1772,11 +1772,13 @@ fn finish_parsing_ability_def_help<'a>(
Ok((MadeProgress, (type_def, def_region), state))
}
#[allow(clippy::too_many_arguments)]
fn parse_expr_operator<'a>(
min_indent: u32,
options: ExprParseOptions,
mut expr_state: ExprState<'a>,
loc_op: Loc<BinOp>,
line_indent: u32,
arena: &'a Bump,
state: State<'a>,
initial_state: State<'a>,
@ -1821,7 +1823,8 @@ fn parse_expr_operator<'a>(
}
BinOp::Assignment => {
let expr_region = expr_state.expr.region;
let indented_more = min_indent + 1;
let indented_more = line_indent + 1;
let call = expr_state
.validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction)
@ -2028,12 +2031,18 @@ fn parse_expr_end<'a>(
state: State<'a>,
initial_state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let inner_min_indent = if options.suffixed_found {
min_indent + 1
} else {
min_indent
};
let parser = skip_first!(
crate::blankspace::check_indent(EExpr::IndentEnd, options.suffixed_found),
crate::blankspace::check_indent(EExpr::IndentEnd),
loc_term_or_underscore(options)
);
match parser.parse(arena, state.clone(), min_indent) {
match parser.parse(arena, state.clone(), inner_min_indent) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((
_,
@ -2135,6 +2144,7 @@ fn parse_expr_end<'a>(
Err((NoProgress, _)) => {
let before_op = state.clone();
// try an operator
let line_indent = state.line_indent();
match loc!(operator()).parse(arena, state.clone(), min_indent) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((_, loc_op, state)) => {
@ -2145,6 +2155,7 @@ fn parse_expr_end<'a>(
options,
expr_state,
loc_op,
line_indent,
arena,
state,
initial_state,

View file

@ -46,7 +46,7 @@ pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
move |arena, state: State<'a>, min_indent| {
let (_, pattern, state) = loc_pattern_help_help().parse(arena, state, min_indent)?;
let (_, pattern, state) = loc_pattern_help_help(true).parse(arena, state, min_indent)?;
let pattern_state = state.clone();
@ -82,11 +82,13 @@ pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>>
}
}
fn loc_pattern_help_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
fn loc_pattern_help_help<'a>(
can_have_arguments: bool,
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
loc_ident_pattern_help(true),
loc_ident_pattern_help(can_have_arguments),
loc!(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
@ -143,7 +145,8 @@ fn loc_tag_pattern_arg<'a>(
min_indent,
)?;
let (_, loc_pat, state) = loc_parse_tag_pattern_arg().parse(arena, state, min_indent)?;
// Cannot have arguments here, pass `false` to make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
let (_, loc_pat, state) = loc_pattern_help_help(false).parse(arena, state, min_indent)?;
let Loc { region, value } = loc_pat;
@ -194,21 +197,6 @@ pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPatt
)
}
fn loc_parse_tag_pattern_arg<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
loc_ident_pattern_help(false),
loc!(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
)),
loc!(string_like_pattern_help()),
loc!(number_pattern_help())
)
}
fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
then(
loc!(collection_trailing_sep_e!(

View file

@ -2,9 +2,9 @@
name = "test_syntax-fuzz"
publish = false
authors.workspace = true
edition.workspace = true
version.workspace = true
version = "0.0.0"
authors = ["Automatically generated"]
edition = "2021"
[package.metadata]
cargo-fuzz = true
@ -12,8 +12,8 @@ cargo-fuzz = true
[dependencies]
test_syntax = { path = "../../test_syntax" }
bumpalo.workspace = true
libfuzzer-sys.workspace = true
bumpalo = { version = "3.12.0", features = ["collections"] }
libfuzzer-sys = "0.4"
# Prevent this from interfering with workspaces
[workspace]

View file

@ -0,0 +1,54 @@
When(
@5-10 Apply(
@5-7 Tag(
"Ok",
),
[
@8-10 List(
[],
),
],
Space,
),
[
WhenBranch {
patterns: [
@18-23 SpaceBefore(
Apply(
@18-20 Tag(
"Ok",
),
[
@21-23 List(
[],
),
],
),
[
Newline,
],
),
],
value: @27-29 Record(
[],
),
guard: None,
},
WhenBranch {
patterns: [
@34-35 SpaceBefore(
Underscore(
"",
),
[
Newline,
],
),
],
value: @39-41 Record(
[],
),
guard: None,
},
],
)

View file

@ -0,0 +1,3 @@
when Ok [] is
Ok [] -> {}
_ -> {}

View file

@ -491,6 +491,7 @@ mod test_snapshots {
pass/when_in_function_python_style_indent.expr,
pass/when_in_parens.expr,
pass/when_in_parens_indented.expr,
pass/when_result_list.expr,
pass/when_with_alternative_patterns.expr,
pass/when_with_function_application.expr,
pass/when_with_negative_numbers.expr,

View file

@ -1,5 +1,5 @@
[package]
name = "roc_lang_srv"
name = "roc_language_server"
version = "0.0.1"
edition = "2021"

View file

@ -33,50 +33,49 @@ use self::{analysed_doc::ModuleIdToUrl, tokens::Token};
pub const HIGHLIGHT_TOKENS_LEGEND: &[SemanticTokenType] = Token::LEGEND;
/// Contains hashmaps of info about all modules that were analyzed
#[derive(Debug)]
pub(super) struct ModulesInfo {
subs: Mutex<HashMap<ModuleId, Subs>>,
exposed: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
docs: VecMap<ModuleId, ModuleDocumentation>,
struct ModulesInfo {
subs_by_module: HashMap<ModuleId, Mutex<Subs>>,
exposed_by_module: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
docs_by_module: HashMap<ModuleId, ModuleDocumentation>,
}
impl ModulesInfo {
fn with_subs<F, A>(&self, mod_id: &ModuleId, f: F) -> Option<A>
fn with_subs<F, A>(&self, module_id: &ModuleId, f: F) -> Option<A>
where
F: FnOnce(&mut Subs) -> A,
{
self.subs.lock().get_mut(mod_id).map(f)
let subs = self.subs_by_module.get(module_id)?;
Some(f(&mut subs.lock()))
}
/// Transforms some of the raw data from the analysis into a state that is
/// more useful during processes like completion.
fn from_analysis(
fn get_docs(&self, module_id: &ModuleId) -> Option<&ModuleDocumentation> {
self.docs_by_module.get(module_id)
}
fn from_loaded_module(
exposes: MutMap<ModuleId, Vec<(Symbol, Variable)>>,
typechecked: &MutMap<ModuleId, CheckedModule>,
docs_by_module: VecMap<ModuleId, ModuleDocumentation>,
) -> 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.
// example: A imports B. B exposes [add, multiply, divide] and A will store a reference to that list.
let exposed = exposes
let exposed_by_module = exposes
.into_iter()
.map(|(module_id, symbols)| (module_id, Arc::new(symbols)))
.collect::<HashMap<_, _>>();
// Combine the subs from all modules
let all_subs = Mutex::new(
typechecked
.iter()
.map(|(module_id, checked_module)| {
(*module_id, checked_module.solved_subs.0.clone())
})
.collect::<HashMap<_, _>>(),
);
let subs_by_module = typechecked
.iter()
.map(|(module_id, checked_module)| {
(*module_id, checked_module.solved_subs.0.clone().into())
})
.collect::<HashMap<_, _>>();
let docs_by_module = docs_by_module.into_iter().collect();
ModulesInfo {
subs: all_subs,
exposed,
docs: docs_by_module,
subs_by_module,
exposed_by_module,
docs_by_module,
}
}
}
@ -84,15 +83,14 @@ impl ModulesInfo {
#[derive(Debug, Clone)]
pub(super) struct AnalyzedModule {
exposed_imports: Vec<(Symbol, Variable)>,
/// imports are grouped by which module they come from
imports: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
imports_by_module: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
module_id: ModuleId,
interns: Interns,
subs: Subs,
abilities: AbilitiesStore,
declarations: Declarations,
modules_info: Arc<ModulesInfo>,
// We need this because ModuleIds are not stable between compilations, so a ModuleId visible to
// ModuleIds are not stable between compilations, so a ModuleId visible to
// one module may not be true global to the language server.
module_id_to_url: ModuleIdToUrl,
}
@ -164,7 +162,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(
let modules_info = Arc::new(ModulesInfo::from_loaded_module(
exposes,
&typechecked,
docs_by_module,
@ -304,7 +302,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
let analyzed_module = AnalyzedModule {
exposed_imports,
imports,
imports_by_module: imports,
subs,
abilities,
declarations,
@ -342,7 +340,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
(
id,
self.modules_info
.exposed
.exposed_by_module
.get(&id)
.unwrap_or(&Arc::new(vec![]))
.clone(),

View file

@ -69,6 +69,7 @@ impl DocInfo {
let end = Position::new(self.line_info.num_lines(), 0);
Range::new(start, end)
}
pub fn get_prefix_at_position(&self, position: Position) -> String {
let position = position.to_roc_position(&self.line_info);
let offset = position.offset as usize;
@ -82,6 +83,7 @@ impl DocInfo {
String::from(symbol)
}
pub fn format(&self) -> Option<Vec<TextEdit>> {
let source = &self.source;
let arena = &Bump::new();
@ -175,10 +177,12 @@ impl AnalyzedDocument {
let (region, var) = roc_can::traverse::find_closest_type_at(pos, declarations)?;
//TODO:Can this be integrated into find closest type? is it even worth it?
let docs_opt = self
.symbol_at(position)
.and_then(|symb| modules_info.docs.get(module_id)?.get_doc_for_symbol(&symb));
//TODO: Can this be integrated into "find closest type"? Is it worth it?
let docs_opt = self.symbol_at(position).and_then(|symbol| {
modules_info
.get_docs(module_id)?
.get_doc_for_symbol(&symbol)
});
let type_str = format_var_type(var, &mut subs.clone(), module_id, interns);
@ -238,7 +242,7 @@ impl AnalyzedDocument {
subs,
declarations,
exposed_imports,
imports,
imports_by_module: imports,
modules_info,
..
} = self.module()?;
@ -299,7 +303,7 @@ impl AnalyzedDocument {
&mut subs.clone(),
module_id,
interns,
modules_info.docs.get(module_id),
modules_info.get_docs(module_id),
exposed_imports,
);
Some(completions)

View file

@ -77,7 +77,7 @@ pub(super) fn get_module_completion_items(
mod_id,
interns,
exposed_symbols,
modules_info.docs.get(mod_id),
modules_info.get_docs(mod_id),
modules_info,
)),
..Default::default()
@ -90,7 +90,7 @@ pub(super) fn get_module_completion_items(
exposed_symbols,
modules_info,
mod_id,
modules_info.docs.get(mod_id),
modules_info.get_docs(mod_id),
interns,
)
} else {

View file

@ -146,12 +146,14 @@ If you would like your organization to become an official sponsor of Roc's devel
We'd also like to express our gratitude to our generous [individual sponsors](https://github.com/sponsors/roc-lang/)! A special thanks to those sponsoring $25/month or more:
<ul id="individual-sponsors">
<li><a href="https://github.com/krzysztofgb">Krzysztof G.</a>
<li><a href="https://github.com/smores56">Sam Mohr</a>
<li><a href="https://github.com/megakilo">Steven Chen</a>
<li><a href="https://github.com/asteroidb612">Drew Lazzeri</a>
<li><a href="https://github.com/mrmizz">Alex Binaei</a>
<li><a href="https://github.com/jonomallanyk">Jono Mallanyk</a>
<li><a href="https://github.com/AngeloChecked">Angelo Ceccato</a></li>
<li><a href="https://github.com/noverby">Niclas Overby</a></li>
<li><a href="https://github.com/krzysztofgb">Krzysztof G.</a></li>
<li><a href="https://github.com/smores56">Sam Mohr</a></li>
<li><a href="https://github.com/megakilo">Steven Chen</a></li>
<li><a href="https://github.com/asteroidb612">Drew Lazzeri</a></li>
<li><a href="https://github.com/mrmizz">Alex Binaei</a></li>
<li><a href="https://github.com/jonomallanyk">Jono Mallanyk</a></li>
<li><a href="https://github.com/chris-packett">Chris Packett</a></li>
<li><a href="https://github.com/jamesbirtles">James Birtles</a></li>
<li><a href="https://github.com/Ivo-Balbaert">Ivo Balbaert</a></li>