From ae7d15f5e3af8e60ed6d7672c6a66c47105c5c19 Mon Sep 17 00:00:00 2001 From: Kai Schmidt Date: Mon, 24 Jun 2024 13:33:08 -0700 Subject: [PATCH] allow for a name in scopes --- site/src/editor/utils.rs | 2 +- src/ast.rs | 13 +++++++-- src/compile/mod.rs | 25 +++++++++++------ src/compile/modifier.rs | 2 +- src/format.rs | 59 +++++++++++++++++++++++++++++----------- src/lex.rs | 15 ++-------- src/lsp.rs | 2 +- src/parse.rs | 24 ++++++++++------ 8 files changed, 90 insertions(+), 52 deletions(-) diff --git a/site/src/editor/utils.rs b/site/src/editor/utils.rs index 282c06b6f..b7d100dd8 100644 --- a/site/src/editor/utils.rs +++ b/site/src/editor/utils.rs @@ -1025,7 +1025,7 @@ pub fn progressive_strings(input: &str) -> Vec { Item::Binding(binding) => { lines.push(vec![binding.span().as_str(&inputs, |s| s.into())]) } - Item::TestScope(items) => lines.push(vec![items.span.as_str(&inputs, |s| s.into())]), + Item::Module(items) => lines.push(vec![items.span.as_str(&inputs, |s| s.into())]), Item::Import(import) => lines.push(vec![import.span().as_str(&inputs, |s| s.into())]), } } diff --git a/src/ast.rs b/src/ast.rs index 2dcc424c3..29f3ea019 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -19,8 +19,8 @@ pub enum Item { Binding(Binding), /// An import Import(Import), - /// A test scope - TestScope(Sp>), + /// A scope + Module(Sp), } /// A binding @@ -51,6 +51,15 @@ impl Binding { } } +/// A scoped module +#[derive(Debug, Clone)] +pub struct ScopedModule { + /// The name of the scope + pub name: Option>, + /// The items + pub items: Vec, +} + /// An import #[derive(Debug, Clone)] pub struct Import { diff --git a/src/compile/mod.rs b/src/compile/mod.rs index 4a78f9287..7e643aadf 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -173,8 +173,8 @@ pub(crate) struct Scope { #[derive(Clone, Copy, PartialEq, Eq)] enum ScopeKind { - /// A scope at the top level of a file - File, + /// A scope at the top level of a file or in a named module + Module, /// A temporary scope, probably for a macro Temp, /// A test scope between `---`s @@ -184,7 +184,7 @@ enum ScopeKind { impl Default for Scope { fn default() -> Self { Self { - kind: ScopeKind::File, + kind: ScopeKind::Module, file_path: None, comment: None, names: IndexMap::new(), @@ -493,9 +493,16 @@ code: (words.iter()).any(|w| matches!(&w.value, Word::SemanticComment(_))) } let mut lines = match item { - Item::TestScope(items) => { + Item::Module(module) => { prev_comment.take(); - self.in_scope(ScopeKind::Test, |env| env.items(items.value, true))?; + let is_test = + (module.value.name.as_ref()).map_or(true, |name| name.value == "test"); + let scope_kind = if is_test { + ScopeKind::Test + } else { + ScopeKind::Module + }; + self.in_scope(scope_kind, |env| env.items(module.value.items, true))?; return Ok(()); } Item::Words(lines) => lines, @@ -770,7 +777,7 @@ code: format!("Cycle detected importing {}", path.to_string_lossy()), )); } - let import = self.in_scope(ScopeKind::File, |env| { + let import = self.in_scope(ScopeKind::Module, |env| { env.load_str_src(&input, &path).map(drop) })?; self.imports.insert(path.clone(), import); @@ -1504,8 +1511,8 @@ code: } let mut hit_file = false; for scope in self.higher_scopes.iter().rev() { - if scope.kind == ScopeKind::File { - if hit_file || self.scope.kind == ScopeKind::File { + if scope.kind == ScopeKind::Module { + if hit_file || self.scope.kind == ScopeKind::Module { break; } hit_file = true; @@ -2169,7 +2176,7 @@ code: .or_else(|| { self.higher_scopes .last() - .filter(|_| self.scope.kind != ScopeKind::File) + .filter(|_| self.scope.kind != ScopeKind::Module) .and_then(|scope| scope.names.get(name)) }) .map_or(true, |l| l.index != local.index) diff --git a/src/compile/modifier.rs b/src/compile/modifier.rs index a4e6437a3..5fb95ae86 100644 --- a/src/compile/modifier.rs +++ b/src/compile/modifier.rs @@ -1290,7 +1290,7 @@ impl Compiler { Item::Import(import) => self .import(import, None) .map_err(|e| e.trace_macro(span.clone()))?, - Item::TestScope(_) => { + Item::Module(_) => { self.add_error(span.clone(), "Macros may not generate test scopes") } }; diff --git a/src/format.rs b/src/format.rs index d2730467c..f16d330ff 100644 --- a/src/format.rs +++ b/src/format.rs @@ -487,7 +487,7 @@ type GlyphMap = Vec<(CodeSpan, (Loc, Loc))>; impl<'a> Formatter<'a> { fn format_top_items(mut self, items: &[Item]) -> (String, GlyphMap) { - self.format_items(items); + self.format_items(items, 0); let mut output = self.output; while output.ends_with('\n') { output.pop(); @@ -497,10 +497,12 @@ impl<'a> Formatter<'a> { } (output, self.glyph_map) } - fn format_items(&mut self, items: &[Item]) { - for item in items { - self.format_item(item); - self.output.push('\n'); + fn format_items(&mut self, items: &[Item], depth: usize) { + for (i, item) in items.iter().enumerate() { + if i > 0 || depth > 0 { + self.newline(depth); + } + self.format_item(item, depth); } // Align end-of-line comments if self.config.align_comments && !self.end_of_line_comments.is_empty() { @@ -573,19 +575,37 @@ impl<'a> Formatter<'a> { self.output = new_output; } } - fn format_item(&mut self, item: &Item) { + fn newline(&mut self, depth: usize) { + self.output.push('\n'); + self.indent(depth); + } + fn indent(&mut self, depth: usize) { + for _ in 0..self.config.multiline_indent * depth { + self.output.push(' '); + } + } + fn format_item(&mut self, item: &Item, depth: usize) { + if depth == 0 { + dbg!(item); + } match item { - Item::TestScope(items) => { + Item::Module(m) => { self.prev_import_function = None; self.output.push_str("---"); - self.output.push('\n'); - self.format_items(&items.value); + if let Some(name) = &m.value.name { + self.push(&name.span, &name.value); + } + self.format_items(&m.value.items, depth + 1); + if self.output.ends_with('\n') { + self.output.pop(); + } + self.newline(depth); self.output.push_str("---"); } Item::Words(lines) => { self.prev_import_function = None; let lines = unsplit_words(lines.iter().cloned().flat_map(split_words).collect()); - self.format_multiline_words(&lines, false, false, 0); + self.format_multiline_words(&lines, false, false, false, depth); } Item::Binding(binding) => { match binding.words.first().map(|w| &w.value) { @@ -624,7 +644,7 @@ impl<'a> Formatter<'a> { .unwrap_or_else(|| binding.arrow_span.clone()); let mut lines = unsplit_words(split_words(binding.words.clone())); if lines.len() == 1 { - self.format_words(&lines[0], true, 0); + self.format_words(&lines[0], true, depth); } else { lines.push(Vec::new()); self.format_words( @@ -635,7 +655,7 @@ impl<'a> Formatter<'a> { closed: true, }))], true, - 0, + depth, ); } } @@ -873,7 +893,13 @@ impl<'a> Formatter<'a> { self.output.pop(); } } - self.format_multiline_words(&arr.lines, arr.signature.is_none(), true, depth + 1); + self.format_multiline_words( + &arr.lines, + arr.signature.is_none(), + true, + true, + depth + 1, + ); if arr.boxes { self.output.push('}'); } else { @@ -915,7 +941,7 @@ impl<'a> Formatter<'a> { self.output.pop(); } } - self.format_multiline_words(&func.lines, false, true, depth + 1); + self.format_multiline_words(&func.lines, false, true, true, depth + 1); self.output.push(')'); } Word::Pack(pack) => { @@ -954,7 +980,7 @@ impl<'a> Formatter<'a> { self.output.pop(); } } - self.format_multiline_words(&br.value.lines, false, false, depth + 1); + self.format_multiline_words(&br.value.lines, false, false, true, depth + 1); if any_multiline && br.value.lines.last().is_some_and(|line| !line.is_empty()) && !self.output.trim_end_matches(' ').ends_with('\n') @@ -1115,6 +1141,7 @@ impl<'a> Formatter<'a> { lines: &[Vec>], allow_compact: bool, allow_leading_space: bool, + allow_trailing_newline: bool, depth: usize, ) { if lines.is_empty() { @@ -1174,7 +1201,7 @@ impl<'a> Formatter<'a> { self.format_words(line, true, depth); } - if !compact { + if allow_trailing_newline && !compact { if depth > 0 && !lines.iter().last().is_some_and(|line| line.is_empty()) { self.output.push('\n'); } diff --git a/src/lex.rs b/src/lex.rs index db865bec4..28f06f421 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -775,11 +775,6 @@ impl<'a> Lexer<'a> { } fn run(mut self) -> (Vec>, Vec>) { use {self::AsciiToken::*, Token::*}; - // Initial scope delimiters - let start = self.loc; - if self.next_chars_exact(["-", "-", "-"]) { - self.end(TripleMinus, start); - } // Main loop 'main: loop { let start = self.loc; @@ -801,6 +796,7 @@ impl<'a> Lexer<'a> { "_" => self.end(Underscore, start), "|" => self.end(Bar, start), ";" => self.end(Semicolon, start), + "-" if self.next_chars_exact(["-", "-"]) => self.end(TripleMinus, start), "'" if self.next_char_exact("'") => { if let Some(swiz) = self.array_swizzle() { self.end(ArraySwizzle(swiz), start) @@ -1070,14 +1066,7 @@ impl<'a> Lexer<'a> { self.end(Number, start) } // Newlines - "\n" | "\r\n" => { - self.end(Newline, start); - // Scope delimiters - let start = self.loc; - if self.next_chars_exact(["-", "-", "-"]) { - self.end(TripleMinus, start); - } - } + "\n" | "\r\n" => self.end(Newline, start), " " | "\t" => { while self.next_char_exact(" ") || self.next_char_exact("\t") {} self.end(Spaces, start) diff --git a/src/lsp.rs b/src/lsp.rs index cc8c9217a..01c9536de 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -173,7 +173,7 @@ impl Spanner { let mut spans = Vec::new(); for item in items { match item { - Item::TestScope(items) => spans.extend(self.items_spans(&items.value)), + Item::Module(m) => spans.extend(self.items_spans(&m.value.items)), Item::Words(lines) => { for line in lines { spans.extend(self.words_spans(line)) diff --git a/src/parse.rs b/src/parse.rs index f7fb80be7..a0fc81ebf 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -201,7 +201,7 @@ pub fn parse( next_output_comment: 0, depth: 0, }; - let items = parser.items(true); + let items = parser.items(false); if parser.errors.is_empty() && parser.index < parser.tokens.len() { parser.errors.push( parser @@ -282,13 +282,13 @@ impl<'i> Parser<'i> { )); self.errors.push(err); } - fn items(&mut self, parse_scopes: bool) -> Vec { + fn items(&mut self, in_scope: bool) -> Vec { let mut items = Vec::new(); while self.try_exact(Newline).is_some() { self.try_spaces(); } loop { - match self.try_item(parse_scopes) { + match self.try_item(in_scope) { Some(item) => items.push(item), None => { if self.try_exact(Newline).is_none() { @@ -308,7 +308,7 @@ impl<'i> Parser<'i> { } items } - fn try_item(&mut self, parse_scopes: bool) -> Option { + fn try_item(&mut self, in_scope: bool) -> Option { self.try_spaces(); Some(if let Some(binding) = self.try_binding() { Item::Binding(binding) @@ -319,18 +319,24 @@ impl<'i> Parser<'i> { // Convert multiline words into multiple items if !lines.is_empty() { Item::Words(lines) - } else if parse_scopes { + } else { + let backup = self.index; let start = self.try_exact(TripleMinus.into())?; - let items = self.items(false); + self.try_spaces(); + let name = self.try_ident(); + if in_scope && name.is_none() { + self.index = backup; + return None; + } + let items = self.items(true); let span = if let Some(end) = self.try_exact(TripleMinus.into()) { start.merge(end) } else { self.errors.push(self.expected([TripleMinus])); start }; - Item::TestScope(span.sp(items)) - } else { - return None; + let module = ScopedModule { name, items }; + Item::Module(span.sp(module)) } }) }