From ca14f08cb9cd284a34ca8dc33ef9caa61683a927 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Wed, 21 Jul 2021 17:00:30 +0200 Subject: [PATCH 01/10] Add document_symbol support --- vhdl_lang/src/analysis/concurrent.rs | 1 + vhdl_lang/src/analysis/design_unit.rs | 21 +- vhdl_lang/src/ast.rs | 27 +- vhdl_lang/src/ast/display.rs | 6 +- vhdl_lang/src/ast/search.rs | 8 + vhdl_lang/src/data/source.rs | 15 + vhdl_lang/src/lib.rs | 2 +- vhdl_lang/src/syntax/component_declaration.rs | 50 +- vhdl_lang/src/syntax/concurrent_statement.rs | 30 +- vhdl_lang/src/syntax/configuration.rs | 38 +- vhdl_lang/src/syntax/context.rs | 23 +- vhdl_lang/src/syntax/declarative_part.rs | 52 +- vhdl_lang/src/syntax/design_unit.rs | 273 ++- vhdl_lang/src/syntax/test.rs | 42 + vhdl_ls/src/document_symbol.rs | 1997 +++++++++++++++++ vhdl_ls/src/lib.rs | 1 + vhdl_ls/src/stdio_server.rs | 8 + vhdl_ls/src/vhdl_server.rs | 22 + 18 files changed, 2454 insertions(+), 162 deletions(-) create mode 100644 vhdl_ls/src/document_symbol.rs diff --git a/vhdl_lang/src/analysis/concurrent.rs b/vhdl_lang/src/analysis/concurrent.rs index 7ed7b7ea..758c14e4 100644 --- a/vhdl_lang/src/analysis/concurrent.rs +++ b/vhdl_lang/src/analysis/concurrent.rs @@ -65,6 +65,7 @@ impl<'a> AnalyzeContext<'a> { sensitivity_list, decl, statements, + range: _, } = process; if let Some(sensitivity_list) = sensitivity_list { match sensitivity_list { diff --git a/vhdl_lang/src/analysis/design_unit.rs b/vhdl_lang/src/analysis/design_unit.rs index db3422c6..b5878cfd 100644 --- a/vhdl_lang/src/analysis/design_unit.rs +++ b/vhdl_lang/src/analysis/design_unit.rs @@ -75,14 +75,15 @@ impl<'a> AnalyzeContext<'a> { ); if let Some(ref mut list) = unit.generic_clause { - self.analyze_interface_list(&mut primary_region, list, diagnostics)?; + self.analyze_interface_list(&mut primary_region, &mut list.item, diagnostics)?; } if let Some(ref mut list) = unit.port_clause { - self.analyze_interface_list(&mut primary_region, list, diagnostics)?; + self.analyze_interface_list(&mut primary_region, &mut list.item, diagnostics)?; + } + self.analyze_declarative_part(&mut primary_region, &mut unit.decl.item, diagnostics)?; + if let Some(ref mut statements) = unit.statements { + self.analyze_concurrent_part(&mut primary_region, &mut statements.item, diagnostics)?; } - self.analyze_declarative_part(&mut primary_region, &mut unit.decl, diagnostics)?; - self.analyze_concurrent_part(&mut primary_region, &mut unit.statements, diagnostics)?; - *region = primary_region.without_parent(); Ok(()) @@ -148,9 +149,9 @@ impl<'a> AnalyzeContext<'a> { ); if let Some(ref mut list) = unit.generic_clause { - self.analyze_interface_list(&mut primary_region, list, diagnostics)?; + self.analyze_interface_list(&mut primary_region, &mut list.item, diagnostics)?; } - self.analyze_declarative_part(&mut primary_region, &mut unit.decl, diagnostics)?; + self.analyze_declarative_part(&mut primary_region, &mut unit.decl.item, diagnostics)?; if !self.has_package_body() { primary_region.close(diagnostics); @@ -241,8 +242,8 @@ impl<'a> AnalyzeContext<'a> { )), ); - self.analyze_declarative_part(&mut region, &mut unit.decl, diagnostics)?; - self.analyze_concurrent_part(&mut region, &mut unit.statements, diagnostics)?; + self.analyze_declarative_part(&mut region, &mut unit.decl.item, diagnostics)?; + self.analyze_concurrent_part(&mut region, &mut unit.statements.item, diagnostics)?; region.close(diagnostics); Ok(()) } @@ -278,7 +279,7 @@ impl<'a> AnalyzeContext<'a> { let mut region = Region::extend(&package.result().region, Some(&root_region)); - self.analyze_declarative_part(&mut region, &mut unit.decl, diagnostics)?; + self.analyze_declarative_part(&mut region, &mut unit.decl.item, diagnostics)?; region.close(diagnostics); Ok(()) } diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index a2df17ff..414a6247 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -863,6 +863,7 @@ pub struct BlockStatement { pub header: BlockHeader, pub decl: Vec, pub statements: Vec, + pub range: SrcPos, } /// LRM 11.2 Block statement @@ -887,6 +888,7 @@ pub struct ProcessStatement { pub sensitivity_list: Option, pub decl: Vec, pub statements: Vec, + pub range: SrcPos, } /// LRM 11.4 Concurrent procedure call statements @@ -1001,6 +1003,7 @@ pub enum ContextItem { pub struct ContextDeclaration { pub ident: Ident, pub items: ContextClause, + pub range: SrcPos, } /// LRM 4.9 Package instatiation declaration @@ -1010,6 +1013,7 @@ pub struct PackageInstantiation { pub ident: Ident, pub package_name: WithPos, pub generic_map: Option>, + pub range: SrcPos, } /// LRM 7.3 Configuration specification @@ -1097,6 +1101,7 @@ pub struct ConfigurationDeclaration { pub decl: Vec, pub vunit_bind_inds: Vec, pub block_config: BlockConfiguration, + pub range: SrcPos, } /// LRM 3.2 Entity declarations @@ -1104,10 +1109,11 @@ pub struct ConfigurationDeclaration { pub struct EntityDeclaration { pub context_clause: ContextClause, pub ident: Ident, - pub generic_clause: Option>, - pub port_clause: Option>, - pub decl: Vec, - pub statements: Vec, + pub generic_clause: Option>>, + pub port_clause: Option>>, + pub decl: WithPos>, + pub statements: Option>>, + pub range: SrcPos, } /// LRM 3.3 Architecture bodies @@ -1116,8 +1122,9 @@ pub struct ArchitectureBody { pub context_clause: ContextClause, pub ident: Ident, pub entity_name: WithRef, - pub decl: Vec, - pub statements: Vec, + pub decl: WithPos>, + pub statements: WithPos>, + pub range: SrcPos, } /// LRM 4.7 Package declarations @@ -1125,8 +1132,9 @@ pub struct ArchitectureBody { pub struct PackageDeclaration { pub context_clause: ContextClause, pub ident: Ident, - pub generic_clause: Option>, - pub decl: Vec, + pub generic_clause: Option>>, + pub decl: WithPos>, + pub range: SrcPos, } /// LRM 4.8 Package bodies @@ -1134,7 +1142,8 @@ pub struct PackageDeclaration { pub struct PackageBody { pub context_clause: ContextClause, pub ident: WithRef, - pub decl: Vec, + pub decl: WithPos>, + pub range: SrcPos, } /// LRM 13.1 Design units diff --git a/vhdl_lang/src/ast/display.rs b/vhdl_lang/src/ast/display.rs index 1bedb75a..334bc076 100644 --- a/vhdl_lang/src/ast/display.rs +++ b/vhdl_lang/src/ast/display.rs @@ -1039,7 +1039,7 @@ impl Display for EntityDeclaration { if let Some(generic_clause) = &self.generic_clause { let mut first = true; - for generic in generic_clause { + for generic in &generic_clause.item { if first { write!(f, "\n generic (\n {}", generic)?; } else { @@ -1054,7 +1054,7 @@ impl Display for EntityDeclaration { if let Some(port_clause) = &self.port_clause { let mut first = true; - for port in port_clause { + for port in &port_clause.item { if first { write!(f, "\n port (\n {}", port)?; } else { @@ -1078,7 +1078,7 @@ impl Display for PackageDeclaration { write!(f, "package {} is", self.ident)?; let mut first = true; - for generic in generic_clause { + for generic in &generic_clause.item { if first { write!(f, "\n generic (\n {}", generic)?; } else { diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index 7a47d8c4..9cdea64f 100644 --- a/vhdl_lang/src/ast/search.rs +++ b/vhdl_lang/src/ast/search.rs @@ -134,6 +134,13 @@ impl Search for Vec { } } +impl Search for WithPos> { + fn search(&self, searcher: &mut impl Searcher) -> SearchResult { + return_if_found!(&self.item.search(searcher)); + NotFound + } +} + impl Search for Option { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { for decl in self.iter() { @@ -423,6 +430,7 @@ impl Search for LabeledConcurrentStatement { sensitivity_list, decl, statements, + range: _, } = process; return_if_found!(sensitivity_list.search(searcher)); return_if_found!(decl.search(searcher)); diff --git a/vhdl_lang/src/data/source.rs b/vhdl_lang/src/data/source.rs index a08f5580..c65641be 100644 --- a/vhdl_lang/src/data/source.rs +++ b/vhdl_lang/src/data/source.rs @@ -515,6 +515,21 @@ impl SrcPos { } } + /// Combines two lexical positions into a larger lexical position between both. + /// The file name is assumed to be the same. + pub fn combine_into_between(self, other: &dyn AsRef) -> Self { + let other = other.as_ref(); + debug_assert!(self.source == other.source, "Assumes sources are equal"); + + let start = min(self.range.end, other.range.end); + let end = max(self.range.start, other.range.start); + + SrcPos { + source: self.source, + range: Range { start, end }, + } + } + pub fn start(&self) -> Position { self.range.start } diff --git a/vhdl_lang/src/lib.rs b/vhdl_lang/src/lib.rs index c2346320..ad380768 100644 --- a/vhdl_lang/src/lib.rs +++ b/vhdl_lang/src/lib.rs @@ -16,7 +16,7 @@ mod syntax; pub use crate::config::Config; pub use crate::data::{ Diagnostic, Latin1String, Message, MessageHandler, MessagePrinter, MessageType, Position, - Range, Severity, Source, SrcPos, + Range, Severity, Source, SrcPos, WithPos, }; pub use crate::project::{Project, SourceFile}; diff --git a/vhdl_lang/src/syntax/component_declaration.rs b/vhdl_lang/src/syntax/component_declaration.rs index 3e3a9e2f..95e8fab2 100644 --- a/vhdl_lang/src/syntax/component_declaration.rs +++ b/vhdl_lang/src/syntax/component_declaration.rs @@ -9,12 +9,12 @@ use super::common::ParseResult; use super::interface_declaration::{parse_generic_interface_list, parse_port_interface_list}; use super::tokens::{Kind::*, TokenStream}; use crate::ast::{ComponentDeclaration, InterfaceDeclaration}; -use crate::data::{Diagnostic, DiagnosticHandler}; +use crate::data::{Diagnostic, DiagnosticHandler, WithPos}; pub fn parse_optional_generic_list( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, -) -> ParseResult>> { +) -> ParseResult>>> { let mut list = None; loop { let token = stream.peek_expect()?; @@ -22,11 +22,14 @@ pub fn parse_optional_generic_list( Generic => { stream.move_after(&token); let new_list = parse_generic_interface_list(stream, diagnostics)?; - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; if list.is_some() { diagnostics.push(Diagnostic::error(token, "Duplicate generic clause")); } else { - list = Some(new_list); + list = Some(WithPos { + item: new_list, + pos: token.pos.combine_into(&semi_token), + }); } } _ => break, @@ -39,7 +42,7 @@ pub fn parse_optional_generic_list( pub fn parse_optional_port_list( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, -) -> ParseResult>> { +) -> ParseResult>>> { let mut list = None; loop { let token = stream.peek_expect()?; @@ -47,11 +50,14 @@ pub fn parse_optional_port_list( Port => { stream.move_after(&token); let new_list = parse_port_interface_list(stream, diagnostics)?; - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; if list.is_some() { diagnostics.push(Diagnostic::error(token, "Duplicate port clause")); } else { - list = Some(new_list); + list = Some(WithPos { + item: new_list, + pos: token.pos.combine_into(&semi_token), + }); } } Generic => { @@ -92,8 +98,8 @@ pub fn parse_component_declaration( Ok(ComponentDeclaration { ident, - generic_list: generic_list.unwrap_or_default(), - port_list: port_list.unwrap_or_default(), + generic_list: generic_list.map_or(Vec::new(), |x| x.item), + port_list: port_list.map_or(Vec::new(), |x| x.item), }) } @@ -102,7 +108,7 @@ mod tests { use super::*; use crate::ast::Ident; - use crate::syntax::test::Code; + use crate::syntax::test::{source_range, Code}; fn to_component( ident: Ident, @@ -222,7 +228,13 @@ end "Duplicate generic clause" )] ); - assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").generic()])),); + assert_eq!( + result, + Ok(Some(WithPos { + item: vec![code.s1("foo : natural").generic()], + pos: source_range(&code, "generic (", ");"), + })), + ); } #[test] @@ -246,7 +258,13 @@ end "Duplicate port clause" )] ); - assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").port()])),); + assert_eq!( + result, + Ok(Some(WithPos { + item: vec![code.s1("foo : natural").port()], + pos: source_range(&code, "port (", ");"), + })), + ); } #[test] @@ -270,6 +288,12 @@ end "Generic clause must come before port clause" )] ); - assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").port()])),); + assert_eq!( + result, + Ok(Some(WithPos { + item: vec![code.s1("foo : natural").port()], + pos: source_range(&code, "port (", ");"), + })), + ); } } diff --git a/vhdl_lang/src/syntax/concurrent_statement.rs b/vhdl_lang/src/syntax/concurrent_statement.rs index 6cea181b..e93ca366 100644 --- a/vhdl_lang/src/syntax/concurrent_statement.rs +++ b/vhdl_lang/src/syntax/concurrent_statement.rs @@ -27,6 +27,7 @@ use crate::data::*; /// LRM 11.2 Block statement pub fn parse_block_statement( stream: &mut TokenStream, + start_pos: SrcPos, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { let token = stream.peek_expect()?; @@ -48,12 +49,13 @@ pub fn parse_block_statement( stream.expect_kind(Block)?; // @TODO check name stream.pop_if_kind(Identifier)?; - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; Ok(BlockStatement { guard_condition, header, decl, statements, + range: start_pos.combine_into(&semi_token.pos), }) } @@ -154,6 +156,7 @@ fn parse_block_header( pub fn parse_process_statement( stream: &mut TokenStream, postponed: bool, + start_pos: SrcPos, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { let token = stream.peek_expect()?; @@ -207,12 +210,13 @@ pub fn parse_process_statement( stream.expect_kind(Process)?; // @TODO check name stream.pop_if_kind(Identifier)?; - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; Ok(ProcessStatement { postponed, sensitivity_list, decl, statements, + range: start_pos.combine_into(&semi_token.pos), }) } @@ -557,10 +561,10 @@ pub fn parse_concurrent_statement( try_token_kind!( token, Block => { - ConcurrentStatement::Block(parse_block_statement(stream, diagnostics)?) + ConcurrentStatement::Block(parse_block_statement(stream, token.pos, diagnostics)?) }, Process => { - ConcurrentStatement::Process(parse_process_statement(stream, false, diagnostics)?) + ConcurrentStatement::Process(parse_process_statement(stream, false, token.pos, diagnostics)?) }, Component => { let unit = InstantiatedUnit::Component(parse_selected_name(stream)?); @@ -589,9 +593,10 @@ pub fn parse_concurrent_statement( Case => ConcurrentStatement::CaseGenerate(parse_case_generate_statement(stream, diagnostics)?), Assert => ConcurrentStatement::Assert(parse_concurrent_assert_statement(stream, false)?), Postponed => { + let start_pos = token.pos; let token = stream.expect()?; match token.kind { - Process => ConcurrentStatement::Process(parse_process_statement(stream, true, diagnostics)?), + Process => ConcurrentStatement::Process(parse_process_statement(stream, true, start_pos, diagnostics)?), Assert => ConcurrentStatement::Assert(parse_concurrent_assert_statement(stream, true)?), With => ConcurrentStatement::Assignment(parse_selected_signal_assignment(stream, true)?), _ => { @@ -704,7 +709,7 @@ pub fn parse_labeled_concurrent_statement( mod tests { use super::*; use crate::ast::{Alternative, AssertStatement, DelayMechanism, Selection}; - use crate::syntax::test::Code; + use crate::syntax::test::{source_range, Code}; #[test] fn test_concurrent_procedure() { @@ -799,6 +804,7 @@ end block; label: Some(code.s1("name2").ident()), statement: ConcurrentStatement::ProcedureCall(call), }], + range: source_range(&code, "block", "end block;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -824,6 +830,7 @@ end block name; }, decl: vec![], statements: vec![], + range: source_range(&code, "block", "end block name;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -849,6 +856,7 @@ end block; }, decl: vec![], statements: vec![], + range: source_range(&code, "block", "end block;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -874,6 +882,7 @@ end block; }, decl: vec![], statements: vec![], + range: source_range(&code, "block", "end block;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -903,6 +912,7 @@ end block; }, decl: vec![], statements: vec![], + range: source_range(&code, "block", "end block;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -923,6 +933,7 @@ end process; sensitivity_list: None, decl: vec![], statements: vec![], + range: source_range(&code, "process", "end process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -943,6 +954,7 @@ end process name; sensitivity_list: None, decl: vec![], statements: vec![], + range: source_range(&code, "process", "end process name;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -963,6 +975,7 @@ end process; sensitivity_list: None, decl: vec![], statements: vec![], + range: source_range(&code, "postponed", "end process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -983,6 +996,7 @@ end postponed process; sensitivity_list: None, decl: vec![], statements: vec![], + range: source_range(&code, "postponed", "process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -1004,6 +1018,7 @@ end postponed process; sensitivity_list: None, decl: Vec::new(), statements: Vec::new(), + range: source_range(&code, "process", "process;"), }; assert_eq!( diagnostics, @@ -1032,6 +1047,7 @@ end process; ])), decl: vec![], statements: vec![], + range: source_range(&code, "process", "end process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -1053,6 +1069,7 @@ end process; sensitivity_list: Some(SensitivityList::Names(Vec::new())), decl: Vec::new(), statements: Vec::new(), + range: source_range(&code, "process", "end process;"), }; assert_eq!( diagnostics, @@ -1084,6 +1101,7 @@ end process; code.s1("foo <= true;").sequential_statement(), code.s1("wait;").sequential_statement(), ], + range: source_range(&code, "process", "end process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); diff --git a/vhdl_lang/src/syntax/configuration.rs b/vhdl_lang/src/syntax/configuration.rs index 5e9413d4..a4b696e7 100644 --- a/vhdl_lang/src/syntax/configuration.rs +++ b/vhdl_lang/src/syntax/configuration.rs @@ -268,7 +268,7 @@ pub fn parse_configuration_declaration( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - stream.expect_kind(Configuration)?; + let configuration_token = stream.expect_kind(Configuration)?; let ident = stream.expect_ident()?; stream.expect_kind(Of)?; let entity_name = parse_selected_name(stream)?; @@ -301,7 +301,8 @@ pub fn parse_configuration_declaration( if let Some(diagnostic) = error_on_end_identifier_mismatch(&ident, &end_ident) { diagnostics.push(diagnostic) } - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; + let range = configuration_token.pos.combine_into(&semi_token); Ok(ConfigurationDeclaration { context_clause: ContextClause::default(), ident, @@ -309,6 +310,7 @@ pub fn parse_configuration_declaration( decl, vunit_bind_inds, block_config, + range, }) } @@ -351,7 +353,7 @@ pub fn parse_configuration_specification( #[cfg(test)] mod tests { use super::*; - use crate::syntax::test::Code; + use crate::syntax::test::{source_range, Code}; #[test] fn empty_configuration() { @@ -375,7 +377,8 @@ end; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + range: source_range(&code, "configuration", "end;") } ); } @@ -402,7 +405,8 @@ end configuration cfg; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -433,7 +437,8 @@ end configuration cfg; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -466,7 +471,8 @@ end configuration cfg; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -493,7 +499,8 @@ end configuration cfg; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -535,7 +542,8 @@ end configuration cfg; items: vec![], }) ], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -580,7 +588,8 @@ end configuration cfg; items: vec![], }), }),], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -636,7 +645,8 @@ end configuration cfg; items: vec![], }), }),], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -683,7 +693,8 @@ end configuration cfg; vunit_bind_inds: Vec::new(), block_config: None, }),], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -761,7 +772,8 @@ end configuration cfg; block_config: None, }) ], - } + }, + range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } diff --git a/vhdl_lang/src/syntax/context.rs b/vhdl_lang/src/syntax/context.rs index b43dbe58..db92d3f5 100644 --- a/vhdl_lang/src/syntax/context.rs +++ b/vhdl_lang/src/syntax/context.rs @@ -94,6 +94,7 @@ pub fn parse_context( if stream.skip_if_kind(Is)? { let mut items = Vec::with_capacity(16); let end_ident; + let semi_pos; loop { let token = stream.expect()?; try_token_kind!( @@ -104,7 +105,7 @@ pub fn parse_context( End => { stream.pop_if_kind(Context)?; end_ident = stream.pop_optional_ident()?; - stream.expect_kind(SemiColon)?; + semi_pos = Some(stream.expect_kind(SemiColon)?.pos); break; } ) @@ -113,10 +114,17 @@ pub fn parse_context( let ident = to_simple_name(name)?; diagnostics.push_some(error_on_end_identifier_mismatch(&ident, &end_ident)); - + let range = match semi_pos { + Some(semi) => context_token.pos.combine_into(&semi), + None => match stream.peek_expect() { + Ok(token) => context_token.pos.combine_into(&token.pos), + Err(err) => context_token.pos.combine_into(&err.pos), + }, + }; Ok(DeclarationOrReference::Declaration(ContextDeclaration { ident, items, + range, })) } else { // Context reference @@ -140,7 +148,7 @@ mod tests { use super::*; use crate::data::Diagnostic; - use crate::syntax::test::Code; + use crate::syntax::test::{source_range, Code}; #[test] fn test_library_clause_single_name() { @@ -252,7 +260,8 @@ end context ident; code.with_stream_no_diagnostics(parse_context), DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), - items: vec![] + items: vec![], + range: source_range(&code, "context", ";"), }) ); } @@ -278,7 +287,8 @@ end context ident2; context, DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), - items: vec![] + items: vec![], + range: source_range(&code, "context ident", "end context ident2;"), }) ); } @@ -317,7 +327,8 @@ end context; }), code.s1("context foo.ctx;") ), - ] + ], + range: source_range(&code, "context ident", "end context;"), }) ) } diff --git a/vhdl_lang/src/syntax/declarative_part.rs b/vhdl_lang/src/syntax/declarative_part.rs index 63c3d6aa..c855640a 100644 --- a/vhdl_lang/src/syntax/declarative_part.rs +++ b/vhdl_lang/src/syntax/declarative_part.rs @@ -16,30 +16,32 @@ use super::subprogram::parse_subprogram; use super::tokens::{Kind::*, *}; use super::type_declaration::parse_type_declaration; use crate::ast::{ContextClause, Declaration, PackageInstantiation}; -use crate::data::DiagnosticHandler; +use crate::data::{DiagnosticHandler, WithPos}; pub fn parse_package_instantiation(stream: &mut TokenStream) -> ParseResult { - stream.expect_kind(Package)?; + let package_token = stream.expect_kind(Package)?; let ident = stream.expect_ident()?; stream.expect_kind(Is)?; stream.expect_kind(New)?; let package_name = parse_selected_name(stream)?; let token = stream.expect()?; - let generic_map = try_token_kind!( + let (generic_map, semi_token) = try_token_kind!( token, Generic => { stream.expect_kind(Map)?; let association_list = parse_association_list(stream)?; - stream.expect_kind(SemiColon)?; - Some(association_list) + let semi_token = stream.expect_kind(SemiColon)?; + (Some(association_list), semi_token) }, - SemiColon => None); + SemiColon => (None, token)); + let range = package_token.pos.combine_into(&semi_token); Ok(PackageInstantiation { context_clause: ContextClause::default(), ident, package_name, generic_map, + range, }) } @@ -101,7 +103,7 @@ pub fn parse_declarative_part_leave_end_token( | Use | Alias ) - }; + } while let Some(token) = stream.peek()? { match token.kind { @@ -171,12 +173,40 @@ pub fn parse_declarative_part_leave_end_token( Ok(declarations) } +pub fn parse_declarative_part_with_pos_leave_end_token( + stream: &mut TokenStream, + diagnostics: &mut dyn DiagnosticHandler, + start_token: Token, +) -> ParseResult>> { + let decl = parse_declarative_part_leave_end_token(stream, diagnostics)?; + let end_pos = match stream.peek_expect() { + Ok(token) => token.pos, + Err(diag) => diag.pos, + }; + Ok(WithPos { + item: decl, + pos: start_token.pos.combine_into_between(&end_pos), + }) +} + +pub fn parse_declarative_part_with_pos( + stream: &mut TokenStream, + diagnostics: &mut dyn DiagnosticHandler, + start_token: Token, + begin_is_end: bool, +) -> ParseResult>> { + let decl = parse_declarative_part_with_pos_leave_end_token(stream, diagnostics, start_token)?; + let end_token = if begin_is_end { Begin } else { End }; + stream.expect_kind(end_token).log(diagnostics); + Ok(decl) +} + #[cfg(test)] mod tests { use super::*; use crate::ast::{ObjectClass, ObjectDeclaration}; use crate::data::Diagnostic; - use crate::syntax::test::Code; + use crate::syntax::test::{source_range, Code}; #[test] fn package_instantiation() { @@ -191,7 +221,8 @@ package ident is new lib.foo.bar; context_clause: ContextClause::default(), ident: code.s1("ident").ident(), package_name: code.s1("lib.foo.bar").selected_name(), - generic_map: None + generic_map: None, + range: source_range(&code, "package", ";"), } ); } @@ -217,7 +248,8 @@ package ident is new lib.foo.bar foo => bar )") .association_list() - ) + ), + range: source_range(&code, "package", ");"), } ); } diff --git a/vhdl_lang/src/syntax/design_unit.rs b/vhdl_lang/src/syntax/design_unit.rs index 6b2003e9..a0e6946f 100644 --- a/vhdl_lang/src/syntax/design_unit.rs +++ b/vhdl_lang/src/syntax/design_unit.rs @@ -3,21 +3,20 @@ // You can obtain one at http://mozilla.org/MPL/2.0/. // // Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com - -use super::tokens::{Kind::*, TokenStream}; - use super::common::error_on_end_identifier_mismatch; use super::common::ParseResult; use super::component_declaration::{parse_optional_generic_list, parse_optional_port_list}; -use super::concurrent_statement::parse_labeled_concurrent_statements; +use super::concurrent_statement::parse_labeled_concurrent_statements_end_token; use super::configuration::parse_configuration_declaration; use super::context::{ parse_context, parse_library_clause, parse_use_clause, DeclarationOrReference, }; use super::declarative_part::{ - parse_declarative_part, parse_declarative_part_leave_end_token, parse_package_instantiation, + parse_declarative_part_leave_end_token, parse_declarative_part_with_pos, + parse_declarative_part_with_pos_leave_end_token, parse_package_instantiation, }; use super::interface_declaration::parse_generic_interface_list; +use super::tokens::{Kind::*, TokenStream}; use crate::ast::*; use crate::data::*; @@ -27,10 +26,10 @@ pub fn parse_entity_declaration( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - stream.expect_kind(Entity)?; + let entity_token = stream.expect_kind(Entity)?; let ident = stream.expect_ident()?; - stream.expect_kind(Is)?; + let is_token = stream.expect_kind(Is)?; let generic_clause = parse_optional_generic_list(stream, diagnostics)?; let port_clause = parse_optional_port_list(stream, diagnostics)?; @@ -38,17 +37,37 @@ pub fn parse_entity_declaration( let decl = parse_declarative_part_leave_end_token(stream, diagnostics)?; let token = stream.expect()?; + let decl = { + let decl_start = if let Some(ref ports) = port_clause { + &ports.pos + } else if let Some(ref generics) = generic_clause { + &generics.pos + } else { + &is_token.pos + }; + WithPos { + item: decl, + pos: decl_start.clone().combine_into_between(&token.pos), + } + }; let statements = try_token_kind!( token, - End => Vec::new(), - Begin => parse_labeled_concurrent_statements(stream, diagnostics)? + End => None, + Begin => { + let (items, end_token) = parse_labeled_concurrent_statements_end_token(stream, diagnostics)?; + Some(WithPos{ + item: items, + pos: token.pos.combine_into_between(&end_token), + })} ); stream.pop_if_kind(Entity)?; let end_ident = stream.pop_optional_ident()?; if let Some(diagnostic) = error_on_end_identifier_mismatch(&ident, &end_ident) { diagnostics.push(diagnostic); } - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; + let range = entity_token.pos.combine_into(&semi_token); + Ok(EntityDeclaration { context_clause: ContextClause::default(), ident, @@ -56,6 +75,7 @@ pub fn parse_entity_declaration( port_clause, decl, statements, + range, }) } @@ -64,15 +84,22 @@ pub fn parse_architecture_body( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - stream.expect_kind(Architecture)?; + let architecture_token = stream.expect_kind(Architecture)?; let ident = stream.expect_ident()?; stream.expect_kind(Of)?; let entity_name = stream.expect_ident()?; - stream.expect_kind(Is)?; - - let decl = parse_declarative_part(stream, diagnostics, true)?; + let is_token = stream.expect_kind(Is)?; + let decl = parse_declarative_part_with_pos_leave_end_token(stream, diagnostics, is_token)?; + let statements_start = match stream.expect_kind(Begin) { + Ok(begin_token) => begin_token.pos, + Err(err) => { + diagnostics.push(err); + decl.pos.clone() + } + }; - let statements = parse_labeled_concurrent_statements(stream, diagnostics)?; + let (statements, end_token) = + parse_labeled_concurrent_statements_end_token(stream, diagnostics)?; stream.pop_if_kind(Architecture)?; let end_ident = stream.pop_optional_ident()?; @@ -80,14 +107,18 @@ pub fn parse_architecture_body( diagnostics.push(diagnostic); } - stream.expect_kind(SemiColon)?; - + let semi_token = stream.expect_kind(SemiColon)?; + let range = architecture_token.pos.combine_into(&semi_token); Ok(ArchitectureBody { context_clause: ContextClause::default(), ident, entity_name: entity_name.into_ref(), decl, - statements, + statements: WithPos { + item: statements, + pos: statements_start.combine_into_between(&end_token.pos), + }, + range, }) } @@ -96,32 +127,38 @@ pub fn parse_package_declaration( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - stream.expect_kind(Package)?; + let package_token = stream.expect_kind(Package)?; let ident = stream.expect_ident()?; - stream.expect_kind(Is)?; + let mut decl_start_token = stream.expect_kind(Is)?; let generic_clause = { - if stream.skip_if_kind(Generic)? { + if let Some(generic_token) = stream.pop_if_kind(Generic)? { let decl = parse_generic_interface_list(stream, diagnostics)?; - stream.expect_kind(SemiColon)?; - Some(decl) + decl_start_token = stream.expect_kind(SemiColon)?; + Some(WithPos { + item: decl, + pos: generic_token.pos.combine_into(&decl_start_token), + }) } else { None } }; - let decl = parse_declarative_part(stream, diagnostics, false)?; + + let decl = parse_declarative_part_with_pos(stream, diagnostics, decl_start_token, false)?; stream.pop_if_kind(Package)?; let end_ident = stream.pop_optional_ident()?; if let Some(diagnostic) = error_on_end_identifier_mismatch(&ident, &end_ident) { diagnostics.push(diagnostic); } stream.pop_if_kind(Identifier)?; - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; + let range = package_token.pos.combine_into(&semi_token); Ok(PackageDeclaration { context_clause: ContextClause::default(), ident, generic_clause, decl, + range, }) } @@ -130,12 +167,12 @@ pub fn parse_package_body( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - stream.expect_kind(Package)?; + let package_token = stream.expect_kind(Package)?; stream.expect_kind(Body)?; let ident = stream.expect_ident()?; - stream.expect_kind(Is)?; - let decl = parse_declarative_part(stream, diagnostics, false)?; + let is_token = stream.expect_kind(Is)?; + let decl = parse_declarative_part_with_pos(stream, diagnostics, is_token, false)?; if stream.skip_if_kind(Package)? { stream.expect_kind(Body)?; } @@ -143,12 +180,13 @@ pub fn parse_package_body( if let Some(diagnostic) = error_on_end_identifier_mismatch(&ident, &end_ident) { diagnostics.push(diagnostic); } - stream.expect_kind(SemiColon)?; - + let semi_token = stream.expect_kind(SemiColon)?; + let range = package_token.pos.combine_into(&semi_token); Ok(PackageBody { context_clause: ContextClause::default(), ident: ident.into_ref(), decl, + range, }) } @@ -278,9 +316,11 @@ pub fn parse_design_file( #[cfg(test)] mod tests { use super::*; - use crate::data::Diagnostic; - use crate::syntax::test::{check_diagnostics, check_no_diagnostics, Code}; + use crate::syntax::test::{ + check_diagnostics, check_no_diagnostics, source_range, source_range_between, Code, + }; + use pretty_assertions::assert_eq; fn parse_str(code: &str) -> (Code, DesignFile, Vec) { let code = Code::new(code); @@ -309,14 +349,18 @@ mod tests { } /// An simple entity with only a name - fn simple_entity(ident: Ident) -> AnyDesignUnit { + fn simple_entity(code: &Code, ident: &str) -> AnyDesignUnit { AnyDesignUnit::Primary(AnyPrimaryUnit::Entity(EntityDeclaration { context_clause: ContextClause::default(), - ident, + ident: code.s1(ident).ident(), generic_clause: None, port_clause: None, - decl: vec![], - statements: vec![], + decl: WithPos { + item: vec![], + pos: source_range_between(&code, &format!("entity {} is", &ident), "end"), + }, + statements: None, + range: source_range(code, &format!("entity {}", &ident), ";"), })) } @@ -328,10 +372,7 @@ entity myent is end entity; ", ); - assert_eq!( - design_file.design_units, - [simple_entity(code.s1("myent").ident())] - ); + assert_eq!(design_file.design_units, [simple_entity(&code, "myent")]); let (code, design_file) = parse_ok( " @@ -339,10 +380,7 @@ entity myent is end entity myent; ", ); - assert_eq!( - design_file.design_units, - [simple_entity(code.s1("myent").ident())] - ); + assert_eq!(design_file.design_units, [simple_entity(&code, "myent")]); } #[test] @@ -359,10 +397,17 @@ end entity; EntityDeclaration { context_clause: ContextClause::default(), ident: code.s1("myent").ident(), - generic_clause: Some(Vec::new()), + generic_clause: Some(WithPos { + item: Vec::new(), + pos: source_range(&code, "generic (", ");") + }), port_clause: None, - decl: vec![], - statements: vec![], + decl: WithPos { + item: vec![], + pos: source_range_between(&code, ");", "end"), + }, + statements: None, + range: source_range(&code, "entity", "end entity;"), } ); } @@ -386,10 +431,17 @@ end entity; item: code.symbol("myent"), pos: code.s1("myent").pos() }, - generic_clause: Some(vec![code.s1("runner_cfg : string").generic()]), + generic_clause: Some(WithPos { + item: vec![code.s1("runner_cfg : string").generic()], + pos: source_range(&code, "generic (", ");"), + }), port_clause: None, - decl: vec![], - statements: vec![], + decl: WithPos { + item: vec![], + pos: source_range_between(&code, ");", "end"), + }, + statements: None, + range: source_range(&code, "entity", "end entity;"), } ); } @@ -409,9 +461,16 @@ end entity; context_clause: ContextClause::default(), ident: code.s1("myent").ident(), generic_clause: None, - port_clause: Some(vec![]), - decl: vec![], - statements: vec![], + port_clause: Some(WithPos { + item: vec![], + pos: source_range(&code, "port (", ");"), + }), + decl: WithPos { + item: vec![], + pos: source_range_between(&code, ");", "end"), + }, + statements: None, + range: source_range(&code, "entity", "end entity;"), } ); } @@ -432,8 +491,15 @@ end entity; ident: code.s1("myent").ident(), generic_clause: None, port_clause: None, - decl: vec![], - statements: vec![], + decl: WithPos { + item: vec![], + pos: source_range_between(&code, "is", "begin"), + }, + statements: Some(WithPos { + item: vec![], + pos: source_range_between(&code, "begin", "end"), + }), + range: source_range(&code, "entity", "end entity;"), } ); } @@ -454,8 +520,12 @@ end entity; ident: code.s1("myent").ident(), generic_clause: None, port_clause: None, - decl: code.s1("constant foo : natural := 0;").declarative_part(), - statements: vec![], + decl: WithPos { + item: code.s1("constant foo : natural := 0;").declarative_part(), + pos: source_range_between(&code, "myent is", "end entity"), + }, + statements: None, + range: source_range(&code, "entity", "end entity;"), } ); } @@ -477,8 +547,15 @@ end entity; ident: code.s1("myent").ident(), generic_clause: None, port_clause: None, - decl: vec![], - statements: vec![code.s1("check(clk, valid);").concurrent_statement()], + decl: WithPos { + item: vec![], + pos: source_range_between(&code, "is", "begin"), + }, + statements: Some(WithPos { + item: vec![code.s1("check(clk, valid);").concurrent_statement()], + pos: source_range_between(&code, "begin", "end") + }), + range: source_range(&code, "entity", "end entity;"), } ); } @@ -503,22 +580,29 @@ end; assert_eq!( design_file.design_units, [ - simple_entity(code.s1("myent").ident()), - simple_entity(code.s1("myent2").ident()), - simple_entity(code.s1("myent3").ident()), - simple_entity(code.s1("myent4").ident()) + simple_entity(&code, "myent"), + simple_entity(&code, "myent2"), + simple_entity(&code, "myent3"), + simple_entity(&code, "myent4") ] ); } // An simple entity with only a name - fn simple_architecture(ident: Ident, entity_name: Ident) -> AnyDesignUnit { + fn simple_architecture(code: &Code, ident: &str, entity_name: &str) -> AnyDesignUnit { AnyDesignUnit::Secondary(AnySecondaryUnit::Architecture(ArchitectureBody { context_clause: ContextClause::default(), - ident, - entity_name: entity_name.into_ref(), - decl: Vec::new(), - statements: vec![], + ident: code.s1(ident).ident(), + entity_name: code.s1(entity_name).ident().into_ref(), + decl: WithPos { + item: Vec::new(), + pos: source_range_between(code, "is", "begin"), + }, + statements: WithPos { + item: vec![], + pos: source_range_between(code, "begin", "end"), + }, + range: source_range(code, &format!("architecture {}", &ident), ";"), })) } @@ -533,10 +617,7 @@ end architecture; ); assert_eq!( design_file.design_units, - [simple_architecture( - code.s1("arch_name").ident(), - code.s1("myent").ident() - )] + [simple_architecture(&code, "arch_name", "myent")] ); } @@ -551,10 +632,7 @@ end architecture arch_name; ); assert_eq!( design_file.design_units, - [simple_architecture( - code.s1("arch_name").ident(), - code.s1("myent").ident() - )] + [simple_architecture(&code, "arch_name", "myent")] ); } @@ -569,10 +647,7 @@ end; ); assert_eq!( design_file.design_units, - [simple_architecture( - code.s1("arch_name").ident(), - code.s1("myent").ident() - )] + [simple_architecture(&code, "arch_name", "myent")] ); } @@ -590,7 +665,11 @@ end package; context_clause: ContextClause::default(), ident: code.s1("pkg_name").ident(), generic_clause: None, - decl: vec![], + decl: WithPos { + item: vec![], + pos: source_range_between(&code, "is", "end package;"), + }, + range: source_range(&code, "package", "end package;"), } ); } @@ -611,12 +690,16 @@ end package; context_clause: ContextClause::default(), ident: code.s1("pkg_name").ident(), generic_clause: None, - decl: code - .s1("\ + decl: WithPos { + item: code + .s1("\ type foo; constant bar : natural := 0; ") - .declarative_part(), + .declarative_part(), + pos: source_range_between(&code, "is", "end package;") + }, + range: source_range(&code, "package", "end package;"), } ); } @@ -638,11 +721,15 @@ end package; PackageDeclaration { context_clause: ContextClause::default(), ident: code.s1("pkg_name").ident(), - generic_clause: Some(vec![ - code.s1("type foo").generic(), - code.s1("type bar").generic() - ]), - decl: vec![] + generic_clause: Some(WithPos { + item: vec![code.s1("type foo").generic(), code.s1("type bar").generic()], + pos: source_range(&code, "generic (", ");"), + }), + decl: WithPos { + item: vec![], + pos: source_range_between(&code, ");", "end package;"), + }, + range: source_range(&code, "package", "end package;"), } ); } @@ -674,8 +761,12 @@ end entity; ident: code.s1("myent").ident(), generic_clause: None, port_clause: None, - decl: vec![], - statements: vec![], + decl: WithPos { + item: vec![], + pos: source_range_between(&code, "is", "end"), + }, + statements: None, + range: source_range(&code, "entity", "end entity;"), } ))] } diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index 78ad2bf7..d4f234e0 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -597,6 +597,48 @@ impl AsRef for Code { } } +// Create a Range spanning from the first substring occurance of start to +// to the first occurance of end after start (start and end may overlap) +pub fn source_range(code: &Code, start: &str, end: &str) -> SrcPos { + let start = code.s1(start).pos; + let mut end_occurance = 1; + loop { + let end = code.s(end, end_occurance).pos; + if end.range().start.line >= start.range().start.line + && end.range().end.line >= start.range().end.line + { + break start.combine_into(&end); + } + end_occurance += 1; + if end_occurance > 1000 { + panic!("Unable to find range"); + } + } +} + +pub fn source_range_between(code: &Code, start: &str, end: &str) -> SrcPos { + let start = code.s1(start).pos; + let mut end_occurance = 1; + loop { + let end = code.s(end, end_occurance).pos; + if end.range().start.line >= start.range().start.line + && end.range().end.line >= start.range().end.line + { + break SrcPos::new( + code.source().clone(), + Range { + start: start.pos().end(), + end: end.pos().start(), + }, + ); + } + end_occurance += 1; + if end_occurance > 1000 { + panic!("Unable to find range"); + } + } +} + mod tests { use super::*; diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs new file mode 100644 index 00000000..666e5ecd --- /dev/null +++ b/vhdl_ls/src/document_symbol.rs @@ -0,0 +1,1997 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com + +use lsp_types::{DocumentSymbol, DocumentSymbolResponse, SymbolKind, Url}; +use vhdl_lang::ast::*; +use vhdl_lang::Latin1String; +use vhdl_lang::{Source, SrcPos, VHDLParser, WithPos}; + +pub fn nested_document_symbol_response_from_file(uri: &Url) -> Option { + match uri.to_file_path() { + Ok(path) => { + let mut diagnostics = vec![]; + match VHDLParser::default().parse_design_file(&path, &mut diagnostics) { + Ok((_, design_file)) => Some(nested_document_symbol_response(&design_file)), + Err(_) => None, + } + } + _ => None, + } +} + +pub fn nested_document_symbol_response_from_source(source: &Source) -> DocumentSymbolResponse { + let mut diagnostics = vec![]; + let design_file = VHDLParser::default().parse_design_source(&source, &mut diagnostics); + nested_document_symbol_response(&design_file) +} + +pub fn nested_document_symbol_response(design_file: &DesignFile) -> DocumentSymbolResponse { + let mut response = vec![]; + for design_unit in design_file.design_units.iter() { + response.push(design_unit.document_symbol()); + } + DocumentSymbolResponse::from(response) +} + +pub trait HasDocumentSymbol { + fn document_symbol(&self) -> DocumentSymbol; +} + +impl HasDocumentSymbol for AnyDesignUnit { + fn document_symbol(&self) -> DocumentSymbol { + match self { + AnyDesignUnit::Primary(ref unit) => unit.document_symbol(), + AnyDesignUnit::Secondary(ref unit) => unit.document_symbol(), + } + } +} + +impl HasDocumentSymbol for AnyPrimaryUnit { + fn document_symbol(&self) -> DocumentSymbol { + match self { + AnyPrimaryUnit::Entity(ref unit) => unit.document_symbol(), + AnyPrimaryUnit::Configuration(ref unit) => unit.document_symbol(), + AnyPrimaryUnit::Package(ref unit) => unit.document_symbol(), + AnyPrimaryUnit::PackageInstance(ref unit) => unit.document_symbol(), + AnyPrimaryUnit::Context(ref unit) => unit.document_symbol(), + } + } +} + +impl HasDocumentSymbol for EntityDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(&self.context_clause, &mut children); + push_optional_interface_list( + self.generic_clause.as_ref(), + InterfaceListType::Generic, + &mut children, + ); + push_optional_interface_list( + self.port_clause.as_ref(), + InterfaceListType::Port, + &mut children, + ); + push_declarations(&self.decl, &mut children); + if let Some(ref statements) = self.statements { + push_concurrent_statements(statements, &mut children); + } + let mut range = to_lsp_range(&self.range); + if !self.context_clause.is_empty() { + range.start = to_lsp_pos(self.context_clause[0].pos.start()); + } + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("entity")), + kind: SymbolKind::Interface, + deprecated: None, + range, + selection_range: to_lsp_range(&self.ident.pos), + children: none_if_empty(children), + } + } +} + +impl HasDocumentSymbol for ConfigurationDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(&self.context_clause, &mut children); + let mut range = to_lsp_range(&self.range); + if !self.context_clause.is_empty() { + range.start = to_lsp_pos(self.context_clause[0].pos.start()); + } + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("configuration")), + kind: SymbolKind::Constructor, + deprecated: None, + range, + selection_range: to_lsp_range(&self.ident.pos), + children: none_if_empty(children), + } + } +} + +impl HasDocumentSymbol for PackageDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(&self.context_clause, &mut children); + push_optional_interface_list( + self.generic_clause.as_ref(), + InterfaceListType::Generic, + &mut children, + ); + push_declarations(&self.decl, &mut children); + let mut range = to_lsp_range(&self.range); + if !self.context_clause.is_empty() { + range.start = to_lsp_pos(self.context_clause[0].pos.start()); + } + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("package")), + kind: SymbolKind::Package, + deprecated: None, + range, + selection_range: to_lsp_range(&self.ident.pos), + children: none_if_empty(children), + } + } +} + +impl HasDocumentSymbol for PackageInstantiation { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(&self.context_clause, &mut children); + let mut range = to_lsp_range(&self.range); + if !self.context_clause.is_empty() { + range.start = to_lsp_pos(self.context_clause[0].pos.start()); + } + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("package instance")), + kind: SymbolKind::Package, + deprecated: None, + range, + selection_range: to_lsp_range(&self.ident.pos), + children: none_if_empty(children), + } + } +} + +impl HasDocumentSymbol for ContextDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(&self.items, &mut children); + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("context")), + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(&self.range), + selection_range: to_lsp_range(&self.ident.pos), + children: none_if_empty(children), + } + } +} + +impl HasDocumentSymbol for AnySecondaryUnit { + fn document_symbol(&self) -> DocumentSymbol { + match self { + AnySecondaryUnit::PackageBody(ref unit) => unit.document_symbol(), + AnySecondaryUnit::Architecture(ref unit) => unit.document_symbol(), + } + } +} + +impl HasDocumentSymbol for PackageBody { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(&self.context_clause, &mut children); + push_declarations(&self.decl, &mut children); + let mut range = to_lsp_range(&self.range); + if !self.context_clause.is_empty() { + range.start = to_lsp_pos(self.context_clause[0].pos.start()); + } + DocumentSymbol { + name: self.ident.item.item.name_utf8(), + detail: Some(String::from("package body")), + kind: SymbolKind::Package, + deprecated: None, + range, + selection_range: to_lsp_range(&self.ident.item.pos), + children: none_if_empty(children), + } + } +} + +impl HasDocumentSymbol for ArchitectureBody { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + let mut range = to_lsp_range(&self.range); + if !self.context_clause.is_empty() { + range.start = to_lsp_pos(self.context_clause[0].pos.start()); + } + push_context_clause(&self.context_clause, &mut children); + push_declarations(&self.decl, &mut children); + push_concurrent_statements(&self.statements, &mut children); + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(format!( + "architecture of {}", + self.entity_name.item.item.name().to_string() + )), + kind: SymbolKind::Class, + deprecated: None, + range, + selection_range: to_lsp_range(&self.ident.pos), + children: none_if_empty(children), + } + } +} + +fn push_interface_list( + list: &WithPos>, + list_type: InterfaceListType, + symbols: &mut Vec, +) { + if !list.item.is_empty() { + let (name, kind) = match list_type { + InterfaceListType::Port => (String::from("ports"), SymbolKind::Interface), + InterfaceListType::Generic => (String::from("generics"), SymbolKind::Constant), + InterfaceListType::Parameter => (String::from("parameters"), SymbolKind::Interface), + }; + symbols.push(DocumentSymbol { + name, + detail: None, + kind, + deprecated: None, + range: to_lsp_range(&list.pos), + selection_range: lsp_types::Range { + start: to_lsp_pos(list.pos.start()), + end: to_lsp_pos(list.pos.start()), + }, + children: none_if_empty(list.item.iter().map(|x| x.document_symbol()).collect()), + }); + } +} + +fn push_optional_interface_list( + list: Option<&WithPos>>, + list_type: InterfaceListType, + symbols: &mut Vec, +) { + if let Some(list) = list { + push_interface_list(list, list_type, symbols); + } +} + +fn push_declarations(list: &WithPos>, symbols: &mut Vec) { + if !list.item.is_empty() { + let range = to_lsp_range(&list.pos); + symbols.push(DocumentSymbol { + name: String::from("declarations"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range, + selection_range: lsp_types::Range { + start: range.start, + end: range.start, + }, + children: Some( + list.item + .iter() + .map(|decl| decl.document_symbol()) + .collect(), + ), + }); + } +} + +fn push_concurrent_statements( + statements: &WithPos>, + symbols: &mut Vec, +) { + let range = to_lsp_range(&statements.pos); + symbols.push(DocumentSymbol { + name: String::from("statements"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range, + selection_range: lsp_types::Range { + start: range.start, + end: range.start, + }, + children: Some( + statements + .item + .iter() + .map(|stmt| stmt.document_symbol()) + .collect(), + ), + }); +} + +impl HasDocumentSymbol for InterfaceDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + match self { + InterfaceDeclaration::Object(ref object) => object.document_symbol(), + InterfaceDeclaration::File(ref file) => file.document_symbol(), + InterfaceDeclaration::Type(ref ident) => DocumentSymbol { + name: ident.item.name_utf8(), + detail: Some(String::from("Type")), + kind: SymbolKind::TypeParameter, + deprecated: None, + range: to_lsp_range(&ident.pos), + selection_range: to_lsp_range(&ident.pos), + children: None, + }, + InterfaceDeclaration::Subprogram(ref subprogram_declaration, _) => { + subprogram_declaration.document_symbol() + } + InterfaceDeclaration::Package(ref package) => package.document_symbol(), + } + } +} + +impl HasDocumentSymbol for InterfaceObjectDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + let mode = if self.class == ObjectClass::Constant { + String::from("") + } else { + mode_to_string(self.mode) + }; + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(format!( + "{} {}", + mode, + subtype_indication_designator_to_string(&self.subtype_indication) + )), + kind: symbol_kind_from_object_class(self.class), + deprecated: None, + range: to_lsp_range(&self.ident.pos), + selection_range: to_lsp_range(&self.ident.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for InterfaceFileDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(subtype_indication_designator_to_string( + &self.subtype_indication, + )), + kind: SymbolKind::File, + deprecated: None, + range: to_lsp_range(&self.ident.pos), + selection_range: to_lsp_range(&self.ident.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for SubprogramDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + match self { + SubprogramDeclaration::Procedure(procedure) => DocumentSymbol { + name: match procedure.designator.item { + SubprogramDesignator::Identifier(ref symbol) => symbol.name_utf8(), + SubprogramDesignator::OperatorSymbol(ref latin1string) => { + format!("\"{}\"", latin1string.to_string()) + } + }, + detail: Some(String::from("procedure")), + kind: SymbolKind::Method, + deprecated: None, + range: to_lsp_range(&procedure.designator.pos), + selection_range: to_lsp_range(&procedure.designator.pos), + children: None, + }, + SubprogramDeclaration::Function(function) => DocumentSymbol { + name: match function.designator.item { + SubprogramDesignator::Identifier(ref symbol) => symbol.name_utf8(), + SubprogramDesignator::OperatorSymbol(ref latin1string) => { + format!("\"{}\"", latin1string.to_string()) + } + }, + detail: Some(String::from("function")), + kind: SymbolKind::Function, + deprecated: None, + range: to_lsp_range(&function.designator.pos), + selection_range: to_lsp_range(&function.designator.pos), + children: None, + }, + } + } +} + +impl HasDocumentSymbol for WithPos { + fn document_symbol(&self) -> DocumentSymbol { + if &self.item.name_list.len() == &1 { + DocumentSymbol { + name: name_to_string(&self.item.name_list[0].item), + detail: Some(String::from("use")), + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(&self.pos), + selection_range: to_lsp_range(&self.item.name_list[0].pos), + children: None, + } + } else { + DocumentSymbol { + name: String::from("use"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(&self.pos), + selection_range: lsp_types::Range { + start: to_lsp_pos(self.pos.start()), + end: to_lsp_pos(self.pos.start()), + }, + children: none_if_empty( + self.item + .name_list + .iter() + .map(|x| DocumentSymbol { + name: name_to_string(&x.item), + detail: Some(String::from("use")), + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(&x.pos), + selection_range: to_lsp_range(&x.pos), + children: None, + }) + .collect(), + ), + } + } + } +} + +impl HasDocumentSymbol for InterfacePackageDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("Package")), + kind: SymbolKind::Package, + deprecated: None, + range: to_lsp_range(&self.ident.pos), + selection_range: to_lsp_range(&self.ident.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for Declaration { + fn document_symbol(&self) -> DocumentSymbol { + match self { + Declaration::Object(object) => object.document_symbol(), + Declaration::File(file) => file.document_symbol(), + Declaration::Type(type_decl) => type_decl.document_symbol(), + Declaration::Component(component) => component.document_symbol(), + Declaration::Attribute(attribute) => attribute.document_symbol(), + Declaration::Alias(alias) => alias.document_symbol(), + Declaration::SubprogramDeclaration(subprogram) => subprogram.document_symbol(), + Declaration::SubprogramBody(subprogram) => subprogram.specification.document_symbol(), + Declaration::Use(use_decl) => use_decl.document_symbol(), + Declaration::Package(package) => package.document_symbol(), + Declaration::Configuration(configuration) => configuration.document_symbol(), + } + } +} + +impl HasDocumentSymbol for ObjectDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: None, + kind: symbol_kind_from_object_class(self.class), + deprecated: None, + range: to_lsp_range(&self.ident.pos), + selection_range: to_lsp_range(&self.ident.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for FileDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: None, + kind: SymbolKind::File, + deprecated: None, + range: to_lsp_range(&self.ident.pos), + selection_range: to_lsp_range(&self.ident.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for TypeDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("type")), + kind: symbol_kind_from_type_definition(&self.def), + deprecated: None, + range: to_lsp_range(&self.ident.pos), + selection_range: to_lsp_range(&self.ident.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for ComponentDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("component")), + kind: SymbolKind::Interface, + deprecated: None, + range: to_lsp_range(&self.ident.pos), + selection_range: to_lsp_range(&self.ident.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for Attribute { + fn document_symbol(&self) -> DocumentSymbol { + match self { + Attribute::Specification(spec) => DocumentSymbol { + name: spec.ident.item.name_utf8(), + detail: Some(String::from("attribute")), + kind: SymbolKind::Property, + deprecated: None, + range: to_lsp_range(&spec.ident.pos), + selection_range: to_lsp_range(&spec.ident.pos), + children: None, + }, + Attribute::Declaration(decl) => DocumentSymbol { + name: decl.ident.item.name_utf8(), + detail: Some(String::from("attribute")), + kind: SymbolKind::Property, + deprecated: None, + range: to_lsp_range(&decl.ident.pos), + selection_range: to_lsp_range(&decl.ident.pos), + children: None, + }, + } + } +} + +impl HasDocumentSymbol for AliasDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: designator_name_to_string(&self.designator.item), + detail: Some(String::from("alias")), + kind: SymbolKind::TypeParameter, + deprecated: None, + range: to_lsp_range(&self.designator.pos), + selection_range: to_lsp_range(&self.designator.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for ConfigurationSpecification { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: String::from("configuration"), + detail: None, + kind: SymbolKind::Constructor, + deprecated: None, + range: to_lsp_range(&self.spec.component_name.pos), + selection_range: to_lsp_range(&self.spec.component_name.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for LabeledConcurrentStatement { + fn document_symbol(&self) -> DocumentSymbol { + let mut symbol = self.statement.document_symbol(); + if let Some(ref label) = self.label { + symbol.detail = Some(symbol.name); + symbol.name = label.item.name_utf8(); + symbol.range = lsp_types::Range::new(to_lsp_pos(label.pos.start()), symbol.range.end); + symbol.selection_range = to_lsp_range(&label.pos); + } + symbol + } +} + +impl HasDocumentSymbol for ConcurrentStatement { + fn document_symbol(&self) -> DocumentSymbol { + match &self { + ConcurrentStatement::ProcedureCall(stmt) => stmt.document_symbol(), + ConcurrentStatement::Block(stmt) => stmt.document_symbol(), + ConcurrentStatement::Process(stmt) => stmt.document_symbol(), + ConcurrentStatement::Assert(stmt) => stmt.document_symbol(), + ConcurrentStatement::Assignment(stmt) => stmt.document_symbol(), + ConcurrentStatement::Instance(stmt) => stmt.document_symbol(), + ConcurrentStatement::ForGenerate(stmt) => stmt.document_symbol(), + ConcurrentStatement::IfGenerate(stmt) => stmt.document_symbol(), + ConcurrentStatement::CaseGenerate(stmt) => stmt.document_symbol(), + } + } +} + +impl HasDocumentSymbol for ConcurrentProcedureCall { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: name_to_string(&self.call.name.item), + detail: Some(String::from("procedure")), + kind: SymbolKind::Method, + deprecated: None, + range: to_lsp_range(&self.call.name.pos), + selection_range: to_lsp_range(&self.call.name.pos), + children: None, + } + } +} + +// TODO: Add children from generics, ports, declarations and statements +impl HasDocumentSymbol for BlockStatement { + fn document_symbol(&self) -> DocumentSymbol { + let block_start = to_lsp_pos(self.range.start()); + DocumentSymbol { + name: String::from("block"), + detail: None, + kind: SymbolKind::Module, + deprecated: None, + range: to_lsp_range(&self.range), + selection_range: lsp_types::Range { + start: block_start, + end: block_start, + }, + children: None, + } + } +} + +impl HasDocumentSymbol for ProcessStatement { + fn document_symbol(&self) -> DocumentSymbol { + let start_pos = to_lsp_pos(self.range.start()); + DocumentSymbol { + name: String::from("process"), + detail: None, + kind: SymbolKind::Event, + deprecated: None, + range: to_lsp_range(&self.range), + selection_range: lsp_types::Range { + start: start_pos, + end: start_pos, + }, + children: None, + } + } +} + +impl HasDocumentSymbol for ConcurrentAssertStatement { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: String::from("assertion"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: to_lsp_range(&self.statement.condition.pos), + selection_range: to_lsp_range(&self.statement.condition.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for ConcurrentSignalAssignment { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: String::from("assignment"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: to_lsp_range(&self.target.pos), + selection_range: to_lsp_range(&self.target.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for InstantiationStatement { + fn document_symbol(&self) -> DocumentSymbol { + match &self.unit { + InstantiatedUnit::Component(selected_name) => DocumentSymbol { + name: String::from("component"), + detail: None, + kind: SymbolKind::Object, + deprecated: None, + range: to_lsp_range(&selected_name.pos), + selection_range: to_lsp_range(&selected_name.pos), + children: None, + }, + InstantiatedUnit::Entity(selected_name, _) => DocumentSymbol { + name: String::from("entity"), + detail: None, + kind: SymbolKind::Object, + deprecated: None, + range: to_lsp_range(&selected_name.pos), + selection_range: to_lsp_range(&selected_name.pos), + children: None, + }, + InstantiatedUnit::Configuration(selected_name) => DocumentSymbol { + name: String::from("configuration"), + detail: None, + kind: SymbolKind::Object, + deprecated: None, + range: to_lsp_range(&selected_name.pos), + selection_range: to_lsp_range(&selected_name.pos), + children: None, + }, + } + } +} + +impl HasDocumentSymbol for ForGenerateStatement { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: String::from("generate"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: to_lsp_range(&self.index_name.pos), + selection_range: to_lsp_range(&self.index_name.pos), + children: None, + } + } +} + +impl HasDocumentSymbol for IfGenerateStatement { + fn document_symbol(&self) -> DocumentSymbol { + let range = if let Some(cond) = self.conditionals.first() { + to_lsp_range(&cond.condition.pos) + } else { + lsp_types::Range { + start: lsp_types::Position { + line: 0, + character: 0, + }, + end: lsp_types::Position { + line: 0, + character: 0, + }, + } + }; + DocumentSymbol { + name: String::from("generate"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range, + selection_range: range, + children: None, + } + } +} + +impl HasDocumentSymbol for CaseGenerateStatement { + fn document_symbol(&self) -> DocumentSymbol { + let range = to_lsp_range(&self.expression.pos); + DocumentSymbol { + name: String::from("generate"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range, + selection_range: range, + children: None, + } + } +} + +fn symbol_kind_from_type_definition(type_definition: &TypeDefinition) -> SymbolKind { + match type_definition { + TypeDefinition::Enumeration(_) => SymbolKind::Enum, + TypeDefinition::Integer(_) => SymbolKind::TypeParameter, + TypeDefinition::Physical(_) => SymbolKind::TypeParameter, + TypeDefinition::Array(_, _) => SymbolKind::Array, + TypeDefinition::Record(_) => SymbolKind::Struct, + TypeDefinition::Access(_) => SymbolKind::Key, + TypeDefinition::Incomplete(_) => SymbolKind::TypeParameter, + TypeDefinition::File(_) => SymbolKind::File, + TypeDefinition::Protected(_) => SymbolKind::Object, + TypeDefinition::ProtectedBody(_) => SymbolKind::Object, + TypeDefinition::Subtype(_) => SymbolKind::TypeParameter, + } +} + +fn name_to_string(name: &Name) -> String { + match name { + Name::Designator(designator) => designator_name_to_string(&designator.item), + Name::Selected(name, designator) => format!( + "{}.{}", + name_to_string(&name.item), + designator_name_to_string(&designator.item.item) + ), + Name::SelectedAll(name) => format!("{}.{}", name_to_string(&name.item), "all"), + _ => String::from(" "), + } +} + +fn designator_name_to_string(designator: &Designator) -> String { + match designator { + Designator::Identifier(symbol) => symbol.name_utf8(), + Designator::OperatorSymbol(symbol) => format!("\"{}\"", symbol.to_string()), + Designator::Character(character) => { + format!("'{}'", Latin1String::new(&[character.to_owned()])) + } + } +} + +fn mode_to_string(mode: Mode) -> String { + String::from(match mode { + Mode::In => "in", + Mode::Out => "out", + Mode::InOut => "inout", + Mode::Buffer => "buffer", + Mode::Linkage => "linkage", + }) +} + +fn subtype_indication_designator_to_string(subtype_indication: &SubtypeIndication) -> String { + match &subtype_indication.type_mark.item { + SelectedName::Designator(name) => designator_name_to_string(&name.item), + SelectedName::Selected(_, name) => designator_name_to_string(&name.item.item), + } +} + +fn to_lsp_pos(position: vhdl_lang::Position) -> lsp_types::Position { + lsp_types::Position { + line: position.line as u64, + character: position.character as u64, + } +} + +fn to_lsp_range(src_pos: &SrcPos) -> lsp_types::Range { + let range = src_pos.range(); + lsp_types::Range { + start: to_lsp_pos(range.start), + end: to_lsp_pos(range.end), + } +} + +fn symbol_kind_from_object_class(object_class: ObjectClass) -> SymbolKind { + match object_class { + ObjectClass::Signal => SymbolKind::Field, + ObjectClass::Constant => SymbolKind::Constant, + ObjectClass::Variable => SymbolKind::Variable, + ObjectClass::SharedVariable => SymbolKind::Variable, + } +} + +fn none_if_empty(vec: Vec) -> Option> { + if vec.is_empty() { + None + } else { + Some(vec) + } +} + +fn push_context_clause(context_clause: &ContextClause, symbols: &mut Vec) { + if let Some(ref first) = context_clause.first() { + let range = first + .pos + .clone() + .combine_into(context_clause.last().unwrap()); + let mut children = vec![]; + for context_item in context_clause.iter() { + match &context_item.item { + ContextItem::Use(use_clause) => { + for name in use_clause.name_list.iter() { + children.push(DocumentSymbol { + name: name_to_string(&name.item), + detail: Some(String::from("use")), + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(&name.pos), + selection_range: to_lsp_range(&name.pos), + children: None, + }); + } + } + ContextItem::Library(library_clause) => { + for name in library_clause.name_list.iter() { + children.push(DocumentSymbol { + name: name.item.name_utf8(), + detail: Some(String::from("library")), + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(&name.pos), + selection_range: to_lsp_range(&name.pos), + children: None, + }); + } + } + ContextItem::Context(context_reference) => { + for name in context_reference.name_list.iter() { + children.push(DocumentSymbol { + name: name_to_string(&name.item), + detail: Some(String::from("context")), + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(&name.pos), + selection_range: to_lsp_range(&name.pos), + children: None, + }); + } + } + }; + } + symbols.push(DocumentSymbol { + name: String::from("context"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(&range), + selection_range: to_lsp_range(&first.pos), + children: none_if_empty(children), + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use lsp_types; + use pretty_assertions::assert_eq; + use std::convert::TryInto; + use std::io::Write; + use std::path::Path; + use tempfile::NamedTempFile; + use vhdl_lang::Source; + + fn parse_str(code: &str) -> DesignFile { + let mut diagnostics = vec![]; + let source = Source::inline(Path::new("mockpath"), code); + let parser = VHDLParser::default(); + let design_file = parser.parse_design_source(&source, &mut diagnostics); + for err in diagnostics.iter() { + println!("{}", err.show()); + } + if diagnostics.len() > 0 { + panic!("Found errors"); + } + design_file + } + + fn write_source_file(code: &str) -> (Url, NamedTempFile) { + let mut file = NamedTempFile::new().unwrap(); + file.write_all(code.as_bytes()).unwrap(); + ( + Url::from_file_path(file.path().canonicalize().unwrap()).unwrap(), + file, + ) + } + + fn range(code: &str, start: &str, end: &str) -> lsp_types::Range { + find_range(code, start, 1, end, true) + } + + fn range_between(code: &str, start: &str, end: &str) -> lsp_types::Range { + find_range(code, start, 1, end, false) + } + + fn find_range( + code: &str, + start: &str, + start_occurance: usize, + end: &str, + inclusive: bool, + ) -> lsp_types::Range { + let mut start_line = 0; + let mut start_column = 0; + let mut found_start = false; + let mut end_line = 0; + let mut end_column = 0; + let mut line_number = 0; + let mut occurance = 0; + for line in code.lines() { + if !found_start { + if let Some(pos) = line.find(start) { + occurance += 1; + if occurance == start_occurance { + start_column = pos; + if !inclusive { + start_column = start_column + start.len(); + } + start_line = line_number; + found_start = true; + } + } + } + if found_start { + if let Some(pos) = line.find(end) { + end_column = pos; + if inclusive { + end_column = end_column + end.len(); + } + end_line = line_number; + break; + } + } + line_number += 1; + } + + lsp_types::Range { + start: lsp_types::Position { + line: start_line.try_into().unwrap(), + character: start_column.try_into().unwrap(), + }, + end: lsp_types::Position { + line: end_line.try_into().unwrap(), + character: end_column.try_into().unwrap(), + }, + } + } + + fn range1(code: &str, start: &str) -> lsp_types::Range { + range(code, start, start) + } + + fn ieee_context(code: &str) -> DocumentSymbol { + DocumentSymbol { + name: String::from("context"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: range(code, "library ieee", "ieee;"), + selection_range: range(code, "library ieee", "ieee;"), + children: Some(vec![DocumentSymbol { + name: String::from("ieee"), + detail: Some(String::from("library")), + kind: SymbolKind::Namespace, + deprecated: None, + range: range1(code, "ieee"), + selection_range: range1(code, "ieee"), + children: None, + }]), + } + } + + fn simple_generic(code: &str) -> DocumentSymbol { + DocumentSymbol { + name: String::from("generics"), + detail: None, + kind: SymbolKind::Constant, + deprecated: None, + range: range(code, "generic(", ");"), + selection_range: lsp_types::Range { + start: range1(code, "generic").start, + end: range1(code, "generic").start, + }, + children: Some(vec![DocumentSymbol { + name: String::from("g1"), + detail: Some(String::from(" integer")), + kind: SymbolKind::Constant, + deprecated: None, + range: range1(code, "g1"), + selection_range: range1(code, "g1"), + children: None, + }]), + } + } + + fn simple_port(code: &str) -> DocumentSymbol { + DocumentSymbol { + name: String::from("ports"), + detail: None, + kind: SymbolKind::Interface, + deprecated: None, + range: range(code, "port(", ");"), + selection_range: lsp_types::Range { + start: range1(code, "port").start, + end: range1(code, "port").start, + }, + children: Some(vec![DocumentSymbol { + name: String::from("p1"), + detail: Some(String::from("in integer")), + kind: SymbolKind::Field, + deprecated: None, + range: range1(code, "p1"), + selection_range: range1(code, "p1"), + children: None, + }]), + } + } + + fn simple_declaration( + code: &str, + start: &str, + start_occurance: usize, + begin_is_end: bool, + ) -> DocumentSymbol { + let end = if begin_is_end { "begin" } else { "end" }; + let range = find_range(code, start, start_occurance, end, false); + DocumentSymbol { + name: String::from("declarations"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range, + selection_range: lsp_types::Range { + start: range.start, + end: range.start, + }, + children: Some(vec![DocumentSymbol { + name: String::from("decl1"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: range1(code, "decl1"), + selection_range: range1(code, "decl1"), + children: None, + }]), + } + } + + fn simple_statement(code: &str, starts_with_begin: bool) -> DocumentSymbol { + let start = if starts_with_begin { "begin" } else { "end" }; + let stmt_range = range_between(code, start, "end"); + DocumentSymbol { + name: String::from("statements"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: stmt_range, + selection_range: lsp_types::Range { + start: stmt_range.start, + end: stmt_range.start, + }, + children: Some(vec![DocumentSymbol { + name: String::from("stmt1"), + detail: Some(String::from("assignment")), + kind: SymbolKind::Field, + deprecated: None, + range: range(code, "stmt1", "decl1"), + selection_range: range1(code, "stmt1"), + children: None, + }]), + } + } + + #[test] + fn entity_declaration() { + let code = " +library ieee; + +entity ent1 is + generic( + g1 : integer := 3 + ); + port( + p1 : integer + ); + signal decl1 : integer; +begin + stmt1: decl1 <= 1; +end; +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); + assert_eq!( + unit.document_symbol(), + DocumentSymbol { + name: String::from("ent1"), + detail: Some(String::from("entity")), + kind: SymbolKind::Interface, + deprecated: None, + range: range(code, "library", "end;"), + selection_range: range1(code, "ent1"), + children: Some(vec![ + ieee_context(code), + simple_generic(code), + simple_port(code), + simple_declaration(code, ");", 2, true), + simple_statement(code, true), + ]), + } + ); + } + + #[test] + fn configuration_declaration() { + let code = " +library ieee; +configuration cfg of entity_name is + for rtl(0) + end for; +end; +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); + assert_eq!( + unit.document_symbol(), + DocumentSymbol { + name: String::from("cfg"), + detail: Some(String::from("configuration")), + kind: SymbolKind::Constructor, + deprecated: None, + range: range(code, "library", "end;"), + selection_range: range1(code, "cfg"), + children: Some(vec![ieee_context(code)]), + } + ); + } + + #[test] + fn package_declaration() { + let code = " +library ieee; +package pkg is + generic( + g1 : integer := 3 + ); + signal decl1 : integer; +end; +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); + assert_eq!( + unit.document_symbol(), + DocumentSymbol { + name: String::from("pkg"), + detail: Some(String::from("package")), + kind: SymbolKind::Package, + deprecated: None, + range: range(code, "library", "end;"), + selection_range: range1(code, "pkg"), + children: Some(vec![ + ieee_context(code), + simple_generic(code), + simple_declaration(code, ");", 1, false), + ]), + } + ); + } + + #[test] + fn package_instantiation() { + let code = " +library ieee; +package pkg_inst is new work.pkg + generic map( + gen => 1 + ); +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); + assert_eq!( + unit.document_symbol(), + DocumentSymbol { + name: String::from("pkg_inst"), + detail: Some(String::from("package instance")), + kind: SymbolKind::Package, + deprecated: None, + range: range(code, "library", ");"), + selection_range: range1(code, "pkg_inst"), + children: Some(vec![ieee_context(code)]), + } + ); + } + + #[test] + fn context_declaration() { + let code = " +context ctx is + library ieee; +end; +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); + assert_eq!( + unit.document_symbol(), + DocumentSymbol { + name: String::from("ctx"), + detail: Some(String::from("context")), + kind: SymbolKind::Namespace, + deprecated: None, + range: range(code, "context", "end;"), + selection_range: range1(code, "ctx"), + children: Some(vec![ieee_context(code)]), + } + ); + } + + #[test] + fn package_body() { + let code = " +library ieee; +package body pkg is + signal decl1 : integer; +end; +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); + assert_eq!( + unit.document_symbol(), + DocumentSymbol { + name: String::from("pkg"), + detail: Some(String::from("package body")), + kind: SymbolKind::Package, + deprecated: None, + range: range(code, "library", "end;"), + selection_range: range1(code, "pkg"), + children: Some(vec![ + ieee_context(code), + simple_declaration(code, "is", 1, false), + ]), + } + ); + } + + #[test] + fn architecture_body() { + let code = " +library ieee; +architecture rtl of ent is + + signal decl1 : integer; + +begin + + stmt1: decl1 <= 1; + +end architecture; +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); + assert_eq!( + unit.document_symbol(), + DocumentSymbol { + name: String::from("rtl"), + detail: Some(String::from("architecture of ent")), + kind: SymbolKind::Class, + deprecated: None, + range: range(code, "library", "end architecture;"), + selection_range: range1(code, "rtl"), + children: Some(vec![ + ieee_context(code), + simple_declaration(code, "is", 1, true), + simple_statement(code, true) + ]), + } + ); + } + + fn declaration( + code: &str, + name: &str, + detail: Option<&str>, + kind: SymbolKind, + ) -> DocumentSymbol { + DocumentSymbol { + name: String::from(name), + detail: detail.map(|detail| String::from(detail)), + kind, + deprecated: None, + range: range1(code, name), + selection_range: range1(code, name), + children: None, + } + } + + fn declaration2( + name: &str, + detail: Option<&str>, + kind: SymbolKind, + range: lsp_types::Range, + selection_range: lsp_types::Range, + ) -> DocumentSymbol { + DocumentSymbol { + name: String::from(name), + detail: detail.map(|detail| String::from(detail)), + kind, + deprecated: None, + range, + selection_range, + children: None, + } + } + + fn get_declarations(code: &str) -> Vec { + let design_file = parse_str(code); + let document_symbol = design_file.design_units.first().unwrap().document_symbol(); + let mut declarations = None; + for child in document_symbol.children.unwrap().iter() { + if child.name == "declarations" { + declarations = Some(child.children.clone().unwrap()); + break; + } + } + declarations.unwrap() + } + + #[test] + fn object_declaration() { + let code = " +package pkg is + signal o_signal : integer; + constant o_constant : integer := 0; + variable o_variable : integer; + shared variable o_shared_variable : integer; +end; +"; + assert_eq!( + get_declarations(code), + vec![ + declaration(code, "o_signal", None, SymbolKind::Field), + declaration(code, "o_constant", None, SymbolKind::Constant), + declaration(code, "o_variable", None, SymbolKind::Variable), + declaration(code, "o_shared_variable", None, SymbolKind::Variable), + ] + ) + } + + #[test] + fn file_declaration() { + let code = " +package pkg is + file o_file : integer; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration(code, "o_file", None, SymbolKind::File),] + ) + } + + #[test] + fn type_declaration() { + let code = " +package pkg is + type t_enum is (a, b, c); + type t_integer is range 0 to 1; + type t_physical is range 0 to 1e4 units + a; + b = 1000 a; + end units t_physical; + type t_array is array (natural range <>) of integer; + type t_record is record + a : integer; + end record t_record; + type t_access is access integer; + type t_incomp; + type t_file is file of integer; + type t_protected is protected + end protected t_protected; + type t_protected is protected body + end protected body t_protected; + subtype t_subtype is integer range 1 to 2; +end; +"; + assert_eq!( + get_declarations(code), + vec![ + declaration(code, "t_enum", Some("type"), SymbolKind::Enum), + declaration(code, "t_integer", Some("type"), SymbolKind::TypeParameter), + declaration(code, "t_physical", Some("type"), SymbolKind::TypeParameter), + declaration(code, "t_array", Some("type"), SymbolKind::Array), + declaration(code, "t_record", Some("type"), SymbolKind::Struct), + declaration(code, "t_access", Some("type"), SymbolKind::Key), + declaration(code, "t_incomp", Some("type"), SymbolKind::TypeParameter), + declaration(code, "t_file", Some("type"), SymbolKind::File), + declaration(code, "t_protected", Some("type"), SymbolKind::Object), + { + let mut protected_body = + declaration(code, "t_protected", Some("type"), SymbolKind::Object); + protected_body.range.start.line += 2; + protected_body.range.end.line += 2; + protected_body.selection_range.start.line += 2; + protected_body.selection_range.end.line += 2; + protected_body + }, + declaration(code, "t_subtype", Some("type"), SymbolKind::TypeParameter), + ] + ) + } + + #[test] + fn component_declaration() { + let code = " +package pkg is + component m_component is end component; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration( + code, + "m_component", + Some("component"), + SymbolKind::Interface, + ),] + ) + } + + #[test] + fn attribute() { + let code = " +package pkg is + attribute m_attribute : integer; + attribute m_attribute of m_component : component is 0; +end; +"; + assert_eq!( + get_declarations(code), + vec![ + declaration(code, "m_attribute", Some("attribute"), SymbolKind::Property), + { + let mut attr = + declaration(code, "m_attribute", Some("attribute"), SymbolKind::Property); + attr.range.start.line += 1; + attr.range.end.line += 1; + attr.selection_range.start.line += 1; + attr.selection_range.end.line += 1; + attr + } + ] + ) + } + + #[test] + fn alias_declaration() { + let code = " +package pkg is + alias m_alias is o_signal; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration( + code, + "m_alias", + Some("alias"), + SymbolKind::TypeParameter + ),] + ) + } + + #[test] + fn subprogram_declaration() { + let code = " +package pkg is + function m_function return integer; + procedure m_procedure; +end; +"; + assert_eq!( + get_declarations(code), + vec![ + declaration(code, "m_function", Some("function"), SymbolKind::Function), + declaration(code, "m_procedure", Some("procedure"), SymbolKind::Method), + ] + ) + } + + #[test] + fn subprogram_body() { + let code = " +package body pkg is + function m_function return integer is begin end; + procedure m_procedure is begin end; +end; +"; + assert_eq!( + get_declarations(code), + vec![ + declaration(code, "m_function", Some("function"), SymbolKind::Function), + declaration(code, "m_procedure", Some("procedure"), SymbolKind::Method), + ] + ) + } + + #[test] + fn use_declaration() { + let code = " +package pkg is + use work.usepkg.all; + use use_a,use_b, use_c; +end; +"; + let mut multi_use = declaration2( + "use", + None, + SymbolKind::Namespace, + range(code, "use use_a", "c;"), + lsp_types::Range { + start: range(code, "use use_a", "c;").start, + end: range(code, "use use_a", "c;").start, + }, + ); + multi_use.children = Some({ + let mut children = vec![]; + for child in ["use_a", "use_b", "use_c"] { + let use_range = range(&code, child, child); + children.push(declaration2( + child, + Some("use"), + SymbolKind::Namespace, + use_range, + use_range, + )); + } + children + }); + assert_eq!( + get_declarations(code), + vec![ + declaration2( + "work.usepkg.all", + Some("use"), + SymbolKind::Namespace, + range(code, "use work.usepkg", ";"), + range1(code, "work.usepkg.all") + ), + multi_use + ] + ) + } + + #[test] + fn package_instantiation_declaration() { + let code = " +architecture rtl of ent is + package m_package is new gen_package; +begin +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration2( + "m_package", + Some("package instance"), + SymbolKind::Package, + range(code, "package", ";"), + range1(code, "m_package") + )] + ) + } + + #[test] + fn configuration_specification() { + let code = " +architecture rtl of ent is + for all : compname use entity work.ent(sim); +begin +end; +"; + assert_eq!( + get_declarations(code), + vec![{ + let mut conf = declaration(code, "compname", None, SymbolKind::Constructor); + conf.name = String::from("configuration"); + conf + }] + ) + } + + fn statement(code: &str, name: &str, detail: Option<&str>, kind: SymbolKind) -> DocumentSymbol { + DocumentSymbol { + name: String::from(name), + detail: detail.map(|detail| String::from(detail)), + kind, + deprecated: None, + range: range1(code, name), + selection_range: range1(code, name), + children: None, + } + } + + fn statement2( + name: &str, + detail: Option<&str>, + kind: SymbolKind, + range: lsp_types::Range, + selection_range: lsp_types::Range, + ) -> DocumentSymbol { + DocumentSymbol { + name: String::from(name), + detail: detail.map(|detail| String::from(detail)), + kind, + deprecated: None, + range, + selection_range, + children: None, + } + } + + fn get_statements(code: &str) -> Vec { + let design_file = parse_str(code); + let document_symbol = design_file.design_units.first().unwrap().document_symbol(); + let mut statements = None; + for child in document_symbol.children.unwrap().iter() { + if child.name == "statements" { + statements = Some(child.children.clone().unwrap()); + break; + } + } + statements.unwrap() + } + + #[test] + fn concurrent_procedure_call() { + let code = " +architecture rtl of ent is +begin + procedure_call(par); + lbl: procedure_call(par); +end; +"; + assert_eq!( + get_statements(code), + vec![ + statement( + code, + "procedure_call", + Some("procedure"), + SymbolKind::Method + ), + statement2( + "lbl", + Some("procedure_call"), + SymbolKind::Method, + range(code, "lbl", "procedure_call"), + range1(code, "lbl"), + ) + ] + ) + } + + #[test] + fn block_statment() { + let code = " +architecture rtl of ent is +begin + block_lbl: block is + begin + end block; +end; +"; + assert_eq!( + get_statements(code), + vec![statement2( + "block_lbl", + Some("block"), + SymbolKind::Module, + range(code, "block_lbl", ";"), + range1(code, "block_lbl"), + )] + ) + } + + #[test] + fn process_statment() { + let code = " +architecture rtl of ent is +begin + process is begin end process; + lbl: process is begin end process; +end; +"; + assert_eq!( + get_statements(code), + vec![ + statement2( + "process", + None, + SymbolKind::Event, + range(code, "process", "end process;"), + lsp_types::Range { + start: range1(code, "process").start, + end: range1(code, "process").start, + }, + ), + statement2( + "lbl", + Some("process"), + SymbolKind::Event, + range(code, "lbl", "end process;"), + range1(code, "lbl"), + ) + ] + ) + } + + #[test] + fn concurrent_assert_statment() { + let code = " +architecture rtl of ent is +begin + assert true; + lbl: assert false; +end; +"; + assert_eq!( + get_statements(code), + vec![ + statement2( + "assertion", + None, + SymbolKind::Field, + range1(code, "true"), + range1(code, "true"), + ), + statement2( + "lbl", + Some("assertion"), + SymbolKind::Field, + range(code, "lbl", "false"), + range1(code, "lbl"), + ) + ] + ) + } + + #[test] + fn concurrent_signal_assignment() { + let code = " +architecture rtl of ent is +begin + sig <= 1; + lbl: sig <= 2; +end; +"; + assert_eq!( + get_statements(code), + vec![ + statement2( + "assignment", + None, + SymbolKind::Field, + range1(code, "sig"), + range1(code, "sig"), + ), + statement2( + "lbl", + Some("assignment"), + SymbolKind::Field, + range(code, "lbl", "sig"), + range1(code, "lbl"), + ) + ] + ) + } + #[test] + fn instantiation_statement() { + let code = " +architecture rtl of ent is +begin + ent_i: entity work.ent_name(rtl); + cmp_i: component comp_name; + cnf_i: configuration work.cnf_name; +end; +"; + assert_eq!( + get_statements(code), + vec![ + statement2( + "ent_i", + Some("entity"), + SymbolKind::Object, + range(code, "ent_i", "work.ent_name"), + range1(code, "ent_i"), + ), + statement2( + "cmp_i", + Some("component"), + SymbolKind::Object, + range(code, "cmp_i", "comp_name"), + range1(code, "cmp_i"), + ), + statement2( + "cnf_i", + Some("configuration"), + SymbolKind::Object, + range(code, "cnf_i", "work.cnf_name"), + range1(code, "cnf_i"), + ), + ] + ) + } + + #[test] + fn for_generate_statement() { + let code = " +architecture rtl of ent is +begin + lbl: for index_name in 0 to 1 generate + begin + end generate; +end; +"; + assert_eq!( + get_statements(code), + vec![statement2( + "lbl", + Some("generate"), + SymbolKind::Field, + range(code, "lbl", "index_name"), + range1(code, "lbl"), + )] + ) + } + + #[test] + fn if_generate_statement() { + let code = " +architecture rtl of ent is +begin + lbl: if true generate + begin + end generate; +end; +"; + assert_eq!( + get_statements(code), + vec![statement2( + "lbl", + Some("generate"), + SymbolKind::Field, + range(code, "lbl", "true"), + range1(code, "lbl"), + )] + ) + } + + #[test] + fn case_generate_statement() { + let code = " +architecture rtl of ent is +begin + lbl: case expr generate + when others => + end generate; +end; +"; + assert_eq!( + get_statements(code), + vec![statement2( + "lbl", + Some("generate"), + SymbolKind::Field, + range(code, "lbl", "expr"), + range1(code, "lbl"), + )] + ) + } + + #[test] + fn test_nested_document_symbol_response_from_file() { + let code = " +library ieee; + +entity ent1 is +end entity; + +"; + let (source_url, _file) = write_source_file(code); + assert_eq!( + nested_document_symbol_response_from_file(&source_url).unwrap(), + DocumentSymbolResponse::from(vec![DocumentSymbol { + name: String::from("ent1"), + detail: Some(String::from("entity")), + kind: SymbolKind::Interface, + deprecated: None, + range: range(code, "library", "end entity;"), + selection_range: range1(code, "ent1"), + children: Some(vec![ieee_context(code)]), + }]) + ); + } +} diff --git a/vhdl_ls/src/lib.rs b/vhdl_ls/src/lib.rs index 9284f75f..6a844a71 100644 --- a/vhdl_ls/src/lib.rs +++ b/vhdl_ls/src/lib.rs @@ -7,6 +7,7 @@ #[macro_use] extern crate log; +mod document_symbol; mod rpc_channel; mod stdio_server; mod vhdl_server; diff --git a/vhdl_ls/src/stdio_server.rs b/vhdl_ls/src/stdio_server.rs index 91a4d63b..7a34c234 100644 --- a/vhdl_ls/src/stdio_server.rs +++ b/vhdl_ls/src/stdio_server.rs @@ -168,6 +168,14 @@ impl ConnectionRpcChannel { } Err(request) => request, }; + let request = match extract::(request) { + Ok((id, params)) => { + let result = server.text_document_document_symbol(¶ms); + self.send_response(lsp_server::Response::new_ok(id, result)); + return; + } + Err(request) => request, + }; let request = match extract::(request) { Ok((id, _params)) => { server.shutdown_server(); diff --git a/vhdl_ls/src/vhdl_server.rs b/vhdl_ls/src/vhdl_server.rs index 7fd3ef7f..47c27f8e 100644 --- a/vhdl_ls/src/vhdl_server.rs +++ b/vhdl_ls/src/vhdl_server.rs @@ -9,6 +9,9 @@ use lsp_types::*; use fnv::FnvHashMap; use std::collections::hash_map::Entry; +use crate::document_symbol::{ + nested_document_symbol_response_from_file, nested_document_symbol_response_from_source, +}; use crate::rpc_channel::{MessageChannel, RpcChannel}; use std::io; use std::path::{Path, PathBuf}; @@ -208,6 +211,14 @@ impl VHDLServer { pub fn text_document_references(&mut self, params: &ReferenceParams) -> Vec { self.mut_server().text_document_references(¶ms) } + + // textDocument/documentSymbol + pub fn text_document_document_symbol( + &mut self, + params: &DocumentSymbolParams, + ) -> Option { + self.mut_server().text_document_document_symbol(¶ms) + } } struct InitializedVHDLServer { @@ -255,6 +266,7 @@ impl InitializedVHDLServer { definition_provider: Some(true), hover_provider: Some(true), references_provider: Some(true), + document_symbol_provider: Some(true), ..Default::default() }; @@ -444,6 +456,16 @@ impl InitializedVHDLServer { Vec::new() } } + + pub fn text_document_document_symbol( + &mut self, + params: &DocumentSymbolParams, + ) -> Option { + self.project + .get_source(&uri_to_file_name(¶ms.text_document.uri)) + .map(|source| nested_document_symbol_response_from_source(&source)) + .or_else(|| nested_document_symbol_response_from_file(¶ms.text_document.uri)) + } } fn srcpos_to_location(pos: &SrcPos) -> Location { From 111373e68c7c6b90bd7590766d491709abe4a176 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Thu, 22 Jul 2021 10:51:24 +0200 Subject: [PATCH 02/10] Update copyright year --- vhdl_lang/src/analysis/concurrent.rs | 2 +- vhdl_lang/src/analysis/design_unit.rs | 2 +- vhdl_lang/src/ast.rs | 2 +- vhdl_lang/src/ast/display.rs | 2 +- vhdl_lang/src/ast/search.rs | 2 +- vhdl_lang/src/data/source.rs | 2 +- vhdl_lang/src/lib.rs | 2 +- vhdl_lang/src/syntax/component_declaration.rs | 2 +- vhdl_lang/src/syntax/concurrent_statement.rs | 2 +- vhdl_lang/src/syntax/configuration.rs | 2 +- vhdl_lang/src/syntax/context.rs | 2 +- vhdl_lang/src/syntax/declarative_part.rs | 2 +- vhdl_lang/src/syntax/design_unit.rs | 2 +- vhdl_lang/src/syntax/test.rs | 2 +- vhdl_ls/src/document_symbol.rs | 4 ++-- vhdl_ls/src/lib.rs | 2 +- vhdl_ls/src/stdio_server.rs | 2 +- vhdl_ls/src/vhdl_server.rs | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/vhdl_lang/src/analysis/concurrent.rs b/vhdl_lang/src/analysis/concurrent.rs index 758c14e4..36c9a7f8 100644 --- a/vhdl_lang/src/analysis/concurrent.rs +++ b/vhdl_lang/src/analysis/concurrent.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2019, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com // These fields are better explicit than .. since we are forced to consider if new fields should be searched #![allow(clippy::unneeded_field_pattern)] diff --git a/vhdl_lang/src/analysis/design_unit.rs b/vhdl_lang/src/analysis/design_unit.rs index 89990a8f..e21aa51f 100644 --- a/vhdl_lang/src/analysis/design_unit.rs +++ b/vhdl_lang/src/analysis/design_unit.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2019, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::*; use crate::ast::*; diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index 0ce95c98..049fde90 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -3,7 +3,7 @@ // This Source Code Form is subject to the terms of the Mozilla Public // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com // Allowing this, since box_patterns are feature gated: https://github.com/rust-lang/rfcs/pull/469 // Track here: https://github.com/rust-lang/rust/issues/29641 diff --git a/vhdl_lang/src/ast/display.rs b/vhdl_lang/src/ast/display.rs index 334bc076..5afb9e05 100644 --- a/vhdl_lang/src/ast/display.rs +++ b/vhdl_lang/src/ast/display.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com //! Implementation of Display diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index 9cdea64f..9d1bcd7b 100644 --- a/vhdl_lang/src/ast/search.rs +++ b/vhdl_lang/src/ast/search.rs @@ -3,7 +3,7 @@ // This Source Code Form is subject to the terms of the Mozilla Public // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2019, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com // These fields are better explicit than .. since we are forced to consider if new fields should be searched #![allow(clippy::unneeded_field_pattern)] diff --git a/vhdl_lang/src/data/source.rs b/vhdl_lang/src/data/source.rs index 8120d546..54360abf 100644 --- a/vhdl_lang/src/data/source.rs +++ b/vhdl_lang/src/data/source.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::contents::Contents; use super::diagnostic::{Diagnostic, DiagnosticResult}; diff --git a/vhdl_lang/src/lib.rs b/vhdl_lang/src/lib.rs index cf1ca61e..d430667b 100644 --- a/vhdl_lang/src/lib.rs +++ b/vhdl_lang/src/lib.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com #![allow(clippy::upper_case_acronyms)] // False positives with unconditional loops // allow for now diff --git a/vhdl_lang/src/syntax/component_declaration.rs b/vhdl_lang/src/syntax/component_declaration.rs index 95e8fab2..0786ebcc 100644 --- a/vhdl_lang/src/syntax/component_declaration.rs +++ b/vhdl_lang/src/syntax/component_declaration.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::common::error_on_end_identifier_mismatch; use super::common::ParseResult; diff --git a/vhdl_lang/src/syntax/concurrent_statement.rs b/vhdl_lang/src/syntax/concurrent_statement.rs index e93ca366..224370fe 100644 --- a/vhdl_lang/src/syntax/concurrent_statement.rs +++ b/vhdl_lang/src/syntax/concurrent_statement.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::common::error_on_end_identifier_mismatch; use super::common::ParseResult; diff --git a/vhdl_lang/src/syntax/configuration.rs b/vhdl_lang/src/syntax/configuration.rs index a4b696e7..c3cf329a 100644 --- a/vhdl_lang/src/syntax/configuration.rs +++ b/vhdl_lang/src/syntax/configuration.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::common::error_on_end_identifier_mismatch; use super::common::ParseResult; diff --git a/vhdl_lang/src/syntax/context.rs b/vhdl_lang/src/syntax/context.rs index db92d3f5..8fb70f68 100644 --- a/vhdl_lang/src/syntax/context.rs +++ b/vhdl_lang/src/syntax/context.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::common::error_on_end_identifier_mismatch; use super::common::ParseResult; diff --git a/vhdl_lang/src/syntax/declarative_part.rs b/vhdl_lang/src/syntax/declarative_part.rs index c855640a..d81d0162 100644 --- a/vhdl_lang/src/syntax/declarative_part.rs +++ b/vhdl_lang/src/syntax/declarative_part.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::alias_declaration::parse_alias_declaration; use super::attributes::parse_attribute; diff --git a/vhdl_lang/src/syntax/design_unit.rs b/vhdl_lang/src/syntax/design_unit.rs index a0e6946f..baa8dee8 100644 --- a/vhdl_lang/src/syntax/design_unit.rs +++ b/vhdl_lang/src/syntax/design_unit.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::common::error_on_end_identifier_mismatch; use super::common::ParseResult; use super::component_declaration::{parse_optional_generic_list, parse_optional_port_list}; diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index d4f234e0..95f35d6d 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use super::alias_declaration::parse_alias_declaration; use super::common::ParseResult; diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index 666e5ecd..686d6716 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -414,7 +414,7 @@ impl HasDocumentSymbol for SubprogramDeclaration { impl HasDocumentSymbol for WithPos { fn document_symbol(&self) -> DocumentSymbol { - if &self.item.name_list.len() == &1 { + if self.item.name_list.len() == 1 { DocumentSymbol { name: name_to_string(&self.item.name_list[0].item), detail: Some(String::from("use")), @@ -883,7 +883,7 @@ fn none_if_empty(vec: Vec) -> Option> { } } -fn push_context_clause(context_clause: &ContextClause, symbols: &mut Vec) { +fn push_context_clause(context_clause: &[WithPos], symbols: &mut Vec) { if let Some(ref first) = context_clause.first() { let range = first .pos diff --git a/vhdl_ls/src/lib.rs b/vhdl_ls/src/lib.rs index 13546951..ffeaf529 100644 --- a/vhdl_ls/src/lib.rs +++ b/vhdl_ls/src/lib.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com #![allow(clippy::upper_case_acronyms)] #[macro_use] diff --git a/vhdl_ls/src/stdio_server.rs b/vhdl_ls/src/stdio_server.rs index 7a34c234..1a80254a 100644 --- a/vhdl_ls/src/stdio_server.rs +++ b/vhdl_ls/src/stdio_server.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com //! This module handles setting up `VHDLServer` for `stdio` communication. //! It also contains the main event loop for handling incoming messages from the LSP client and diff --git a/vhdl_ls/src/vhdl_server.rs b/vhdl_ls/src/vhdl_server.rs index 47c27f8e..94ca1819 100644 --- a/vhdl_ls/src/vhdl_server.rs +++ b/vhdl_ls/src/vhdl_server.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com use lsp_types::*; From 903c6ad4a15fed3ee64c1935797572408d299517 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Thu, 22 Jul 2021 15:10:40 +0200 Subject: [PATCH 03/10] Fix range for target aggregates --- vhdl_lang/src/syntax/concurrent_statement.rs | 4 +- vhdl_ls/src/document_symbol.rs | 113 ++++++++++++------- 2 files changed, 76 insertions(+), 41 deletions(-) diff --git a/vhdl_lang/src/syntax/concurrent_statement.rs b/vhdl_lang/src/syntax/concurrent_statement.rs index 224370fe..9c78a2e2 100644 --- a/vhdl_lang/src/syntax/concurrent_statement.rs +++ b/vhdl_lang/src/syntax/concurrent_statement.rs @@ -627,7 +627,9 @@ pub fn parse_concurrent_statement( parse_assignment_known_target(stream, name.map_into(Target::Name))? }, LeftPar => { - let target = parse_aggregate_leftpar_known(stream)?.map_into(Target::Aggregate); + let start = token.pos; + let mut target = parse_aggregate_leftpar_known(stream)?.map_into(Target::Aggregate); + target.pos = start.combine_into(&target.pos); let token = stream.expect()?; parse_assignment_or_procedure_call(stream, &token, target)? } diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index 686d6716..1acfaf5b 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -600,7 +600,6 @@ impl HasDocumentSymbol for LabeledConcurrentStatement { fn document_symbol(&self) -> DocumentSymbol { let mut symbol = self.statement.document_symbol(); if let Some(ref label) = self.label { - symbol.detail = Some(symbol.name); symbol.name = label.item.name_utf8(); symbol.range = lsp_types::Range::new(to_lsp_pos(label.pos.start()), symbol.range.end); symbol.selection_range = to_lsp_range(&label.pos); @@ -629,7 +628,7 @@ impl HasDocumentSymbol for ConcurrentProcedureCall { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: name_to_string(&self.call.name.item), - detail: Some(String::from("procedure")), + detail: Some(String::from("procedure call")), kind: SymbolKind::Method, deprecated: None, range: to_lsp_range(&self.call.name.pos), @@ -644,8 +643,8 @@ impl HasDocumentSymbol for BlockStatement { fn document_symbol(&self) -> DocumentSymbol { let block_start = to_lsp_pos(self.range.start()); DocumentSymbol { - name: String::from("block"), - detail: None, + name: String::from(" "), + detail: Some(String::from("block")), kind: SymbolKind::Module, deprecated: None, range: to_lsp_range(&self.range), @@ -662,8 +661,8 @@ impl HasDocumentSymbol for ProcessStatement { fn document_symbol(&self) -> DocumentSymbol { let start_pos = to_lsp_pos(self.range.start()); DocumentSymbol { - name: String::from("process"), - detail: None, + name: String::from(" "), + detail: Some(String::from("process")), kind: SymbolKind::Event, deprecated: None, range: to_lsp_range(&self.range), @@ -679,8 +678,8 @@ impl HasDocumentSymbol for ProcessStatement { impl HasDocumentSymbol for ConcurrentAssertStatement { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { - name: String::from("assertion"), - detail: None, + name: String::from(" "), + detail: Some(String::from("assertion")), kind: SymbolKind::Field, deprecated: None, range: to_lsp_range(&self.statement.condition.pos), @@ -692,9 +691,20 @@ impl HasDocumentSymbol for ConcurrentAssertStatement { impl HasDocumentSymbol for ConcurrentSignalAssignment { fn document_symbol(&self) -> DocumentSymbol { + let name = match &self.target.item { + Target::Name(name) => name.to_string(), + Target::Aggregate(aggregate) => aggregate + .iter() + .map(|x| match x { + ElementAssociation::Positional(pos) => pos.item.to_string(), + ElementAssociation::Named(_, pos) => pos.item.to_string(), + }) + .collect::>() + .join(", "), + }; DocumentSymbol { - name: String::from("assignment"), - detail: None, + name, + detail: Some(String::from("assignment")), kind: SymbolKind::Field, deprecated: None, range: to_lsp_range(&self.target.pos), @@ -706,32 +716,39 @@ impl HasDocumentSymbol for ConcurrentSignalAssignment { impl HasDocumentSymbol for InstantiationStatement { fn document_symbol(&self) -> DocumentSymbol { + let name = String::from("instantiation"); match &self.unit { - InstantiatedUnit::Component(selected_name) => DocumentSymbol { - name: String::from("component"), - detail: None, + InstantiatedUnit::Component(component_name) => DocumentSymbol { + name, + detail: Some(format!("{} : component", component_name.item)), kind: SymbolKind::Object, deprecated: None, - range: to_lsp_range(&selected_name.pos), - selection_range: to_lsp_range(&selected_name.pos), + range: to_lsp_range(&component_name.pos), + selection_range: to_lsp_range(&component_name.pos), children: None, }, - InstantiatedUnit::Entity(selected_name, _) => DocumentSymbol { - name: String::from("entity"), - detail: None, + InstantiatedUnit::Entity(entity_name, architecture_name) => DocumentSymbol { + name, + detail: Some(format!( + "{}{} : entity", + entity_name.item, + architecture_name + .as_ref() + .map_or(String::from(""), |arch| format!("({})", arch)) + )), kind: SymbolKind::Object, deprecated: None, - range: to_lsp_range(&selected_name.pos), - selection_range: to_lsp_range(&selected_name.pos), + range: to_lsp_range(&entity_name.pos), + selection_range: to_lsp_range(&entity_name.pos), children: None, }, - InstantiatedUnit::Configuration(selected_name) => DocumentSymbol { - name: String::from("configuration"), - detail: None, + InstantiatedUnit::Configuration(configuration_name) => DocumentSymbol { + name, + detail: Some(format!("{} : configuration", configuration_name.item)), kind: SymbolKind::Object, deprecated: None, - range: to_lsp_range(&selected_name.pos), - selection_range: to_lsp_range(&selected_name.pos), + range: to_lsp_range(&configuration_name.pos), + selection_range: to_lsp_range(&configuration_name.pos), children: None, }, } @@ -741,8 +758,8 @@ impl HasDocumentSymbol for InstantiationStatement { impl HasDocumentSymbol for ForGenerateStatement { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { - name: String::from("generate"), - detail: None, + name: String::from(" "), + detail: Some(String::from("for generate")), kind: SymbolKind::Field, deprecated: None, range: to_lsp_range(&self.index_name.pos), @@ -769,8 +786,8 @@ impl HasDocumentSymbol for IfGenerateStatement { } }; DocumentSymbol { - name: String::from("generate"), - detail: None, + name: String::from(" "), + detail: Some(String::from("if generate")), kind: SymbolKind::Field, deprecated: None, range, @@ -784,8 +801,8 @@ impl HasDocumentSymbol for CaseGenerateStatement { fn document_symbol(&self) -> DocumentSymbol { let range = to_lsp_range(&self.expression.pos); DocumentSymbol { - name: String::from("generate"), - detail: None, + name: String::from(" "), + detail: Some(String::from("case generate")), kind: SymbolKind::Field, deprecated: None, range, @@ -1821,8 +1838,8 @@ end; get_statements(code), vec![ statement2( - "assertion", - None, + " ", + Some("assertion"), SymbolKind::Field, range1(code, "true"), range1(code, "true"), @@ -1845,14 +1862,16 @@ architecture rtl of ent is begin sig <= 1; lbl: sig <= 2; + (sig1,sig2) <= asd; + lbl2: (sig1,sig2) <= asd; end; "; assert_eq!( get_statements(code), vec![ statement2( - "assignment", - None, + "sig", + Some("assignment"), SymbolKind::Field, range1(code, "sig"), range1(code, "sig"), @@ -1863,7 +1882,21 @@ end; SymbolKind::Field, range(code, "lbl", "sig"), range1(code, "lbl"), - ) + ), + statement2( + "sig1, sig2", + Some("assignment"), + SymbolKind::Field, + range1(code, "(sig1,sig2)"), + range1(code, "(sig1,sig2)"), + ), + statement2( + "lbl2", + Some("assignment"), + SymbolKind::Field, + range(code, "lbl2", "(sig1,sig2)"), + range1(code, "lbl2"), + ), ] ) } @@ -1882,21 +1915,21 @@ end; vec![ statement2( "ent_i", - Some("entity"), + Some("work.ent_name(rtl) : entity"), SymbolKind::Object, range(code, "ent_i", "work.ent_name"), range1(code, "ent_i"), ), statement2( "cmp_i", - Some("component"), + Some("comp_name : component"), SymbolKind::Object, range(code, "cmp_i", "comp_name"), range1(code, "cmp_i"), ), statement2( "cnf_i", - Some("configuration"), + Some("work.cnf_name : configuration"), SymbolKind::Object, range(code, "cnf_i", "work.cnf_name"), range1(code, "cnf_i"), @@ -1963,7 +1996,7 @@ end; get_statements(code), vec![statement2( "lbl", - Some("generate"), + Some("case generate"), SymbolKind::Field, range(code, "lbl", "expr"), range1(code, "lbl"), From 305a85f4c888e9f8f98d9cf117fd8e34d26fc9c4 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Thu, 22 Jul 2021 15:17:44 +0200 Subject: [PATCH 04/10] Update details for document symbols --- vhdl_ls/src/document_symbol.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index 1acfaf5b..102fe8e5 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -1756,12 +1756,12 @@ end; statement( code, "procedure_call", - Some("procedure"), + Some("procedure call"), SymbolKind::Method ), statement2( "lbl", - Some("procedure_call"), + Some("procedure call"), SymbolKind::Method, range(code, "lbl", "procedure_call"), range1(code, "lbl"), @@ -1805,8 +1805,8 @@ end; get_statements(code), vec![ statement2( - "process", - None, + " ", + Some("process"), SymbolKind::Event, range(code, "process", "end process;"), lsp_types::Range { @@ -1952,7 +1952,7 @@ end; get_statements(code), vec![statement2( "lbl", - Some("generate"), + Some("for generate"), SymbolKind::Field, range(code, "lbl", "index_name"), range1(code, "lbl"), @@ -1974,7 +1974,7 @@ end; get_statements(code), vec![statement2( "lbl", - Some("generate"), + Some("if generate"), SymbolKind::Field, range(code, "lbl", "true"), range1(code, "lbl"), From 79cb460f11d0c1b0f6241f9f3d6045b10eb26f4e Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Thu, 22 Jul 2021 15:58:00 +0200 Subject: [PATCH 05/10] Remove custom string functions from documentsymbol --- vhdl_ls/src/document_symbol.rs | 247 ++++++++++++++------------------- 1 file changed, 102 insertions(+), 145 deletions(-) diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index 102fe8e5..39d78a7a 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -6,7 +6,6 @@ use lsp_types::{DocumentSymbol, DocumentSymbolResponse, SymbolKind, Url}; use vhdl_lang::ast::*; -use vhdl_lang::Latin1String; use vhdl_lang::{Source, SrcPos, VHDLParser, WithPos}; pub fn nested_document_symbol_response_from_file(uri: &Url) -> Option { @@ -232,90 +231,6 @@ impl HasDocumentSymbol for ArchitectureBody { } } -fn push_interface_list( - list: &WithPos>, - list_type: InterfaceListType, - symbols: &mut Vec, -) { - if !list.item.is_empty() { - let (name, kind) = match list_type { - InterfaceListType::Port => (String::from("ports"), SymbolKind::Interface), - InterfaceListType::Generic => (String::from("generics"), SymbolKind::Constant), - InterfaceListType::Parameter => (String::from("parameters"), SymbolKind::Interface), - }; - symbols.push(DocumentSymbol { - name, - detail: None, - kind, - deprecated: None, - range: to_lsp_range(&list.pos), - selection_range: lsp_types::Range { - start: to_lsp_pos(list.pos.start()), - end: to_lsp_pos(list.pos.start()), - }, - children: none_if_empty(list.item.iter().map(|x| x.document_symbol()).collect()), - }); - } -} - -fn push_optional_interface_list( - list: Option<&WithPos>>, - list_type: InterfaceListType, - symbols: &mut Vec, -) { - if let Some(list) = list { - push_interface_list(list, list_type, symbols); - } -} - -fn push_declarations(list: &WithPos>, symbols: &mut Vec) { - if !list.item.is_empty() { - let range = to_lsp_range(&list.pos); - symbols.push(DocumentSymbol { - name: String::from("declarations"), - detail: None, - kind: SymbolKind::Field, - deprecated: None, - range, - selection_range: lsp_types::Range { - start: range.start, - end: range.start, - }, - children: Some( - list.item - .iter() - .map(|decl| decl.document_symbol()) - .collect(), - ), - }); - } -} - -fn push_concurrent_statements( - statements: &WithPos>, - symbols: &mut Vec, -) { - let range = to_lsp_range(&statements.pos); - symbols.push(DocumentSymbol { - name: String::from("statements"), - detail: None, - kind: SymbolKind::Field, - deprecated: None, - range, - selection_range: lsp_types::Range { - start: range.start, - end: range.start, - }, - children: Some( - statements - .item - .iter() - .map(|stmt| stmt.document_symbol()) - .collect(), - ), - }); -} - impl HasDocumentSymbol for InterfaceDeclaration { fn document_symbol(&self) -> DocumentSymbol { match self { @@ -343,14 +258,14 @@ impl HasDocumentSymbol for InterfaceObjectDeclaration { let mode = if self.class == ObjectClass::Constant { String::from("") } else { - mode_to_string(self.mode) + self.mode.to_string() }; DocumentSymbol { name: self.ident.item.name_utf8(), detail: Some(format!( "{} {}", mode, - subtype_indication_designator_to_string(&self.subtype_indication) + self.subtype_indication.to_string() // subtype_indication_designator_to_string(&self.subtype_indication) )), kind: symbol_kind_from_object_class(self.class), deprecated: None, @@ -365,9 +280,7 @@ impl HasDocumentSymbol for InterfaceFileDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(subtype_indication_designator_to_string( - &self.subtype_indication, - )), + detail: Some(self.subtype_indication.to_string()), kind: SymbolKind::File, deprecated: None, range: to_lsp_range(&self.ident.pos), @@ -416,7 +329,7 @@ impl HasDocumentSymbol for WithPos { fn document_symbol(&self) -> DocumentSymbol { if self.item.name_list.len() == 1 { DocumentSymbol { - name: name_to_string(&self.item.name_list[0].item), + name: self.item.name_list[0].to_string(), detail: Some(String::from("use")), kind: SymbolKind::Namespace, deprecated: None, @@ -440,7 +353,7 @@ impl HasDocumentSymbol for WithPos { .name_list .iter() .map(|x| DocumentSymbol { - name: name_to_string(&x.item), + name: x.to_string(), detail: Some(String::from("use")), kind: SymbolKind::Namespace, deprecated: None, @@ -571,7 +484,7 @@ impl HasDocumentSymbol for Attribute { impl HasDocumentSymbol for AliasDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { - name: designator_name_to_string(&self.designator.item), + name: self.designator.to_string(), detail: Some(String::from("alias")), kind: SymbolKind::TypeParameter, deprecated: None, @@ -627,7 +540,7 @@ impl HasDocumentSymbol for ConcurrentStatement { impl HasDocumentSymbol for ConcurrentProcedureCall { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { - name: name_to_string(&self.call.name.item), + name: self.call.name.to_string(), detail: Some(String::from("procedure call")), kind: SymbolKind::Method, deprecated: None, @@ -645,7 +558,7 @@ impl HasDocumentSymbol for BlockStatement { DocumentSymbol { name: String::from(" "), detail: Some(String::from("block")), - kind: SymbolKind::Module, + kind: SymbolKind::Namespace, deprecated: None, range: to_lsp_range(&self.range), selection_range: lsp_types::Range { @@ -720,7 +633,7 @@ impl HasDocumentSymbol for InstantiationStatement { match &self.unit { InstantiatedUnit::Component(component_name) => DocumentSymbol { name, - detail: Some(format!("{} : component", component_name.item)), + detail: Some(component_name.item.to_string()), kind: SymbolKind::Object, deprecated: None, range: to_lsp_range(&component_name.pos), @@ -730,7 +643,7 @@ impl HasDocumentSymbol for InstantiationStatement { InstantiatedUnit::Entity(entity_name, architecture_name) => DocumentSymbol { name, detail: Some(format!( - "{}{} : entity", + "{}{}", entity_name.item, architecture_name .as_ref() @@ -744,7 +657,7 @@ impl HasDocumentSymbol for InstantiationStatement { }, InstantiatedUnit::Configuration(configuration_name) => DocumentSymbol { name, - detail: Some(format!("{} : configuration", configuration_name.item)), + detail: Some(configuration_name.item.to_string()), kind: SymbolKind::Object, deprecated: None, range: to_lsp_range(&configuration_name.pos), @@ -812,6 +725,90 @@ impl HasDocumentSymbol for CaseGenerateStatement { } } +fn push_interface_list( + list: &WithPos>, + list_type: InterfaceListType, + symbols: &mut Vec, +) { + if !list.item.is_empty() { + let (name, kind) = match list_type { + InterfaceListType::Port => (String::from("ports"), SymbolKind::Interface), + InterfaceListType::Generic => (String::from("generics"), SymbolKind::Constant), + InterfaceListType::Parameter => (String::from("parameters"), SymbolKind::Interface), + }; + symbols.push(DocumentSymbol { + name, + detail: None, + kind, + deprecated: None, + range: to_lsp_range(&list.pos), + selection_range: lsp_types::Range { + start: to_lsp_pos(list.pos.start()), + end: to_lsp_pos(list.pos.start()), + }, + children: none_if_empty(list.item.iter().map(|x| x.document_symbol()).collect()), + }); + } +} + +fn push_optional_interface_list( + list: Option<&WithPos>>, + list_type: InterfaceListType, + symbols: &mut Vec, +) { + if let Some(list) = list { + push_interface_list(list, list_type, symbols); + } +} + +fn push_declarations(list: &WithPos>, symbols: &mut Vec) { + if !list.item.is_empty() { + let range = to_lsp_range(&list.pos); + symbols.push(DocumentSymbol { + name: String::from("declarations"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range, + selection_range: lsp_types::Range { + start: range.start, + end: range.start, + }, + children: Some( + list.item + .iter() + .map(|decl| decl.document_symbol()) + .collect(), + ), + }); + } +} + +fn push_concurrent_statements( + statements: &WithPos>, + symbols: &mut Vec, +) { + let range = to_lsp_range(&statements.pos); + symbols.push(DocumentSymbol { + name: String::from("statements"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range, + selection_range: lsp_types::Range { + start: range.start, + end: range.start, + }, + children: Some( + statements + .item + .iter() + .map(|stmt| stmt.document_symbol()) + .collect(), + ), + }); +} + fn symbol_kind_from_type_definition(type_definition: &TypeDefinition) -> SymbolKind { match type_definition { TypeDefinition::Enumeration(_) => SymbolKind::Enum, @@ -828,46 +825,6 @@ fn symbol_kind_from_type_definition(type_definition: &TypeDefinition) -> SymbolK } } -fn name_to_string(name: &Name) -> String { - match name { - Name::Designator(designator) => designator_name_to_string(&designator.item), - Name::Selected(name, designator) => format!( - "{}.{}", - name_to_string(&name.item), - designator_name_to_string(&designator.item.item) - ), - Name::SelectedAll(name) => format!("{}.{}", name_to_string(&name.item), "all"), - _ => String::from(" "), - } -} - -fn designator_name_to_string(designator: &Designator) -> String { - match designator { - Designator::Identifier(symbol) => symbol.name_utf8(), - Designator::OperatorSymbol(symbol) => format!("\"{}\"", symbol.to_string()), - Designator::Character(character) => { - format!("'{}'", Latin1String::new(&[character.to_owned()])) - } - } -} - -fn mode_to_string(mode: Mode) -> String { - String::from(match mode { - Mode::In => "in", - Mode::Out => "out", - Mode::InOut => "inout", - Mode::Buffer => "buffer", - Mode::Linkage => "linkage", - }) -} - -fn subtype_indication_designator_to_string(subtype_indication: &SubtypeIndication) -> String { - match &subtype_indication.type_mark.item { - SelectedName::Designator(name) => designator_name_to_string(&name.item), - SelectedName::Selected(_, name) => designator_name_to_string(&name.item.item), - } -} - fn to_lsp_pos(position: vhdl_lang::Position) -> lsp_types::Position { lsp_types::Position { line: position.line as u64, @@ -912,7 +869,7 @@ fn push_context_clause(context_clause: &[WithPos], symbols: &mut Ve ContextItem::Use(use_clause) => { for name in use_clause.name_list.iter() { children.push(DocumentSymbol { - name: name_to_string(&name.item), + name: name.to_string(), detail: Some(String::from("use")), kind: SymbolKind::Namespace, deprecated: None, @@ -925,7 +882,7 @@ fn push_context_clause(context_clause: &[WithPos], symbols: &mut Ve ContextItem::Library(library_clause) => { for name in library_clause.name_list.iter() { children.push(DocumentSymbol { - name: name.item.name_utf8(), + name: name.to_string(), detail: Some(String::from("library")), kind: SymbolKind::Namespace, deprecated: None, @@ -938,7 +895,7 @@ fn push_context_clause(context_clause: &[WithPos], symbols: &mut Ve ContextItem::Context(context_reference) => { for name in context_reference.name_list.iter() { children.push(DocumentSymbol { - name: name_to_string(&name.item), + name: name.to_string(), detail: Some(String::from("context")), kind: SymbolKind::Namespace, deprecated: None, @@ -1785,7 +1742,7 @@ end; vec![statement2( "block_lbl", Some("block"), - SymbolKind::Module, + SymbolKind::Namespace, range(code, "block_lbl", ";"), range1(code, "block_lbl"), )] @@ -1915,21 +1872,21 @@ end; vec![ statement2( "ent_i", - Some("work.ent_name(rtl) : entity"), + Some("work.ent_name(rtl)"), SymbolKind::Object, range(code, "ent_i", "work.ent_name"), range1(code, "ent_i"), ), statement2( "cmp_i", - Some("comp_name : component"), + Some("comp_name"), SymbolKind::Object, range(code, "cmp_i", "comp_name"), range1(code, "cmp_i"), ), statement2( "cnf_i", - Some("work.cnf_name : configuration"), + Some("work.cnf_name"), SymbolKind::Object, range(code, "cnf_i", "work.cnf_name"), range1(code, "cnf_i"), From cff3bc95940f73539624cb57b1fc72d08071c012 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Thu, 22 Jul 2021 16:13:11 +0200 Subject: [PATCH 06/10] Move document_symbol to new requests module --- vhdl_ls/src/lib.rs | 2 +- vhdl_ls/src/requests.rs | 7 +++++++ vhdl_ls/src/{ => requests}/document_symbol.rs | 0 vhdl_ls/src/vhdl_server.rs | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 vhdl_ls/src/requests.rs rename vhdl_ls/src/{ => requests}/document_symbol.rs (100%) diff --git a/vhdl_ls/src/lib.rs b/vhdl_ls/src/lib.rs index ffeaf529..6e13dc92 100644 --- a/vhdl_ls/src/lib.rs +++ b/vhdl_ls/src/lib.rs @@ -8,7 +8,7 @@ #[macro_use] extern crate log; -mod document_symbol; +mod requests; mod rpc_channel; mod stdio_server; mod vhdl_server; diff --git a/vhdl_ls/src/requests.rs b/vhdl_ls/src/requests.rs new file mode 100644 index 00000000..fc005324 --- /dev/null +++ b/vhdl_ls/src/requests.rs @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com + +pub mod document_symbol; \ No newline at end of file diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/requests/document_symbol.rs similarity index 100% rename from vhdl_ls/src/document_symbol.rs rename to vhdl_ls/src/requests/document_symbol.rs diff --git a/vhdl_ls/src/vhdl_server.rs b/vhdl_ls/src/vhdl_server.rs index 94ca1819..6d9ae94d 100644 --- a/vhdl_ls/src/vhdl_server.rs +++ b/vhdl_ls/src/vhdl_server.rs @@ -9,7 +9,7 @@ use lsp_types::*; use fnv::FnvHashMap; use std::collections::hash_map::Entry; -use crate::document_symbol::{ +use crate::requests::document_symbol::{ nested_document_symbol_response_from_file, nested_document_symbol_response_from_source, }; use crate::rpc_channel::{MessageChannel, RpcChannel}; From ed13dfbd828c1f95e8a68494586d210e45370c4b Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sat, 24 Jul 2021 16:06:47 +0200 Subject: [PATCH 07/10] Make individal tests document symbols for type declarations. --- vhdl_ls/src/requests.rs | 2 +- vhdl_ls/src/requests/document_symbol.rs | 157 +++++++++++++++++++++--- 2 files changed, 142 insertions(+), 17 deletions(-) diff --git a/vhdl_ls/src/requests.rs b/vhdl_ls/src/requests.rs index fc005324..766efd56 100644 --- a/vhdl_ls/src/requests.rs +++ b/vhdl_ls/src/requests.rs @@ -4,4 +4,4 @@ // // Copyright (c) 2021, Olof Kraigher olof.kraigher@gmail.com -pub mod document_symbol; \ No newline at end of file +pub mod document_symbol; diff --git a/vhdl_ls/src/requests/document_symbol.rs b/vhdl_ls/src/requests/document_symbol.rs index 39d78a7a..5a64444d 100644 --- a/vhdl_ls/src/requests/document_symbol.rs +++ b/vhdl_ls/src/requests/document_symbol.rs @@ -262,11 +262,7 @@ impl HasDocumentSymbol for InterfaceObjectDeclaration { }; DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(format!( - "{} {}", - mode, - self.subtype_indication.to_string() // subtype_indication_designator_to_string(&self.subtype_indication) - )), + detail: Some(format!("{} {}", mode, self.subtype_indication.to_string())), kind: symbol_kind_from_object_class(self.class), deprecated: None, range: to_lsp_range(&self.ident.pos), @@ -1426,40 +1422,152 @@ end; } #[test] - fn type_declaration() { + fn enum_type_declaration() { let code = " package pkg is type t_enum is (a, b, c); +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration(code, "t_enum", Some("type"), SymbolKind::Enum),] + ) + } + + #[test] + fn integer_type_declaration() { + let code = " +package pkg is type t_integer is range 0 to 1; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration( + code, + "t_integer", + Some("type"), + SymbolKind::TypeParameter + ),] + ) + } + + #[test] + fn physical_type_declaration() { + let code = " +package pkg is type t_physical is range 0 to 1e4 units a; b = 1000 a; end units t_physical; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration( + code, + "t_physical", + Some("type"), + SymbolKind::TypeParameter + ),] + ) + } + + #[test] + fn array_type_declaration() { + let code = " +package pkg is type t_array is array (natural range <>) of integer; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration( + code, + "t_array", + Some("type"), + SymbolKind::Array + ),] + ) + } + + #[test] + fn record_stype_declaration() { + let code = " +package pkg is type t_record is record a : integer; end record t_record; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration( + code, + "t_record", + Some("type"), + SymbolKind::Struct + ),] + ) + } + + #[test] + fn access_type_declaration() { + let code = " +package pkg is type t_access is access integer; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration(code, "t_access", Some("type"), SymbolKind::Key),] + ) + } + + #[test] + fn incomplete_type_declaration() { + let code = " +package pkg is type t_incomp; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration( + code, + "t_incomp", + Some("type"), + SymbolKind::TypeParameter + ),] + ) + } + + #[test] + fn file_type_declaration() { + let code = " +package pkg is type t_file is file of integer; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration(code, "t_file", Some("type"), SymbolKind::File),] + ) + } + + #[test] + fn protected_type_declaration() { + let code = " +package pkg is type t_protected is protected end protected t_protected; type t_protected is protected body end protected body t_protected; - subtype t_subtype is integer range 1 to 2; end; "; assert_eq!( get_declarations(code), vec![ - declaration(code, "t_enum", Some("type"), SymbolKind::Enum), - declaration(code, "t_integer", Some("type"), SymbolKind::TypeParameter), - declaration(code, "t_physical", Some("type"), SymbolKind::TypeParameter), - declaration(code, "t_array", Some("type"), SymbolKind::Array), - declaration(code, "t_record", Some("type"), SymbolKind::Struct), - declaration(code, "t_access", Some("type"), SymbolKind::Key), - declaration(code, "t_incomp", Some("type"), SymbolKind::TypeParameter), - declaration(code, "t_file", Some("type"), SymbolKind::File), declaration(code, "t_protected", Some("type"), SymbolKind::Object), { let mut protected_body = @@ -1470,11 +1578,28 @@ end; protected_body.selection_range.end.line += 2; protected_body }, - declaration(code, "t_subtype", Some("type"), SymbolKind::TypeParameter), ] ) } + #[test] + fn subtype_declaration() { + let code = " +package pkg is + subtype t_subtype is integer range 1 to 2; +end; +"; + assert_eq!( + get_declarations(code), + vec![declaration( + code, + "t_subtype", + Some("type"), + SymbolKind::TypeParameter + ),] + ) + } + #[test] fn component_declaration() { let code = " From 5db6e4b7f0f54293e89b9db0b68cdb04103d41c2 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Mon, 26 Jul 2021 12:30:27 +0200 Subject: [PATCH 08/10] Fix clippy --- vhdl_lang/src/analysis/root.rs | 2 +- vhdl_lang/src/analysis/target.rs | 6 +++--- vhdl_lang/src/analysis/visibility.rs | 21 +++++++++------------ vhdl_lang/src/ast/name_util.rs | 8 ++------ vhdl_lang/src/data/contents.rs | 5 +---- vhdl_lang/src/data/diagnostic.rs | 2 +- vhdl_lang/src/data/symbol_table.rs | 7 +------ vhdl_lang/src/project.rs | 4 ++-- 8 files changed, 20 insertions(+), 35 deletions(-) diff --git a/vhdl_lang/src/analysis/root.rs b/vhdl_lang/src/analysis/root.rs index 353a4074..a8089223 100644 --- a/vhdl_lang/src/analysis/root.rs +++ b/vhdl_lang/src/analysis/root.rs @@ -387,10 +387,10 @@ impl DesignRoot { let result = AnalysisData { diagnostics, + has_circular_dependency, root_region, region, ent, - has_circular_dependency, }; unit.finish(result) diff --git a/vhdl_lang/src/analysis/target.rs b/vhdl_lang/src/analysis/target.rs index fb813f83..d3c4984f 100644 --- a/vhdl_lang/src/analysis/target.rs +++ b/vhdl_lang/src/analysis/target.rs @@ -90,10 +90,10 @@ pub enum AssignmentType { } impl AssignmentType { - fn to_str(&self) -> &str { + fn to_str(self) -> String { match self { - AssignmentType::Signal => "signal", - AssignmentType::Variable => "variable", + AssignmentType::Signal => String::from("signal"), + AssignmentType::Variable => String::from("variable"), } } } diff --git a/vhdl_lang/src/analysis/visibility.rs b/vhdl_lang/src/analysis/visibility.rs index 02ff8db1..f094c6e7 100644 --- a/vhdl_lang/src/analysis/visibility.rs +++ b/vhdl_lang/src/analysis/visibility.rs @@ -203,12 +203,11 @@ impl<'a> Visible<'a> { ); fn last_visible_pos(visible_entity: &VisibleEntityRef) -> u32 { - for pos in visible_entity.visible_pos.iter().rev() { - if let Some(pos) = pos { - return pos.range().start.line; - } + if let Some(pos) = visible_entity.visible_pos.iter().flatten().last() { + pos.range().start.line + } else { + 0 } - 0 } // Sort by last visible pos to make error messages and testing deterministic @@ -216,13 +215,11 @@ impl<'a> Visible<'a> { visible_entities.sort_by_key(|ent| last_visible_pos(*ent)); for visible_entity in visible_entities { - for visible_pos in visible_entity.visible_pos.iter().rev() { - if let Some(pos) = visible_pos { - error.add_related( - pos, - format!("Conflicting name '{}' made visible here", designator), - ); - } + for visible_pos in visible_entity.visible_pos.iter().rev().flatten() { + error.add_related( + visible_pos, + format!("Conflicting name '{}' made visible here", designator), + ); } if let Some(pos) = visible_entity.entity.decl_pos() { error.add_related( diff --git a/vhdl_lang/src/ast/name_util.rs b/vhdl_lang/src/ast/name_util.rs index 639b083a..c626fe10 100644 --- a/vhdl_lang/src/ast/name_util.rs +++ b/vhdl_lang/src/ast/name_util.rs @@ -332,12 +332,8 @@ impl FunctionCall { ref name, ref parameters, } = self; - - if let Some(indexes) = assoc_elems_to_indexes(parameters) { - Some(Name::Indexed(Box::new(name.clone()), indexes)) - } else { - None - } + assoc_elems_to_indexes(parameters) + .map(|indexes| Name::Indexed(Box::new(name.clone()), indexes)) } } diff --git a/vhdl_lang/src/data/contents.rs b/vhdl_lang/src/data/contents.rs index 5a9b9a74..5cd2cdaf 100644 --- a/vhdl_lang/src/data/contents.rs +++ b/vhdl_lang/src/data/contents.rs @@ -136,14 +136,13 @@ fn split_lines(code: &str) -> Vec { while i < bytes.len() { let byte = bytes[i]; + i += 1; if byte == b'\n' { - i += 1; let line = bytes[start..i].to_owned(); let line = unsafe { String::from_utf8_unchecked(line) }; lines.push(line); start = i; } else if byte == b'\r' { - i += 1; let mut line = bytes[start..i].to_owned(); let last = line.len().saturating_sub(1); line[last] = b'\n'; @@ -155,8 +154,6 @@ fn split_lines(code: &str) -> Vec { } start = i; - } else { - i += 1; } } diff --git a/vhdl_lang/src/data/diagnostic.rs b/vhdl_lang/src/data/diagnostic.rs index cf4cd616..1e9b4c42 100644 --- a/vhdl_lang/src/data/diagnostic.rs +++ b/vhdl_lang/src/data/diagnostic.rs @@ -72,7 +72,7 @@ impl Diagnostic { pub fn drain_related(&mut self) -> Vec { let mut diagnostics = Vec::with_capacity(self.related.len()); - let related = std::mem::replace(&mut self.related, Vec::new()); + let related = std::mem::take(&mut self.related); for (pos, msg) in related { diagnostics.push(Diagnostic::new( pos, diff --git a/vhdl_lang/src/data/symbol_table.rs b/vhdl_lang/src/data/symbol_table.rs index 467c55d3..e2fde70d 100644 --- a/vhdl_lang/src/data/symbol_table.rs +++ b/vhdl_lang/src/data/symbol_table.rs @@ -100,12 +100,7 @@ impl SymbolTable { /// and `None` otherwise. pub fn lookup(&self, name: &Latin1String) -> Option { let name_to_symbol = self.name_to_symbol.read(); - if let Some(sym) = name_to_symbol.get(name) { - // Symbol already exists with identical case - Some(sym.clone()) - } else { - None - } + name_to_symbol.get(name).cloned() } /// Inserts a basic identifier and returns a corresponding `Symbol` instance. diff --git a/vhdl_lang/src/project.rs b/vhdl_lang/src/project.rs index 7ec6bd44..2defcb3e 100644 --- a/vhdl_lang/src/project.rs +++ b/vhdl_lang/src/project.rs @@ -142,10 +142,10 @@ impl Project { self.files.insert( source.file_name().to_owned(), SourceFile { - source, library_names, - parser_diagnostics, + source, design_file, + parser_diagnostics, }, ); } From 4a0c30e0606c780a267f96e8c602fd81c587cf51 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Mon, 26 Jul 2021 12:47:35 +0200 Subject: [PATCH 09/10] Rename range field in ast structs to source_range --- vhdl_lang/src/analysis/concurrent.rs | 2 +- vhdl_lang/src/ast.rs | 36 +++++++++++++----- vhdl_lang/src/ast/search.rs | 2 +- vhdl_lang/src/syntax/concurrent_statement.rs | 30 +++++++-------- vhdl_lang/src/syntax/configuration.rs | 24 ++++++------ vhdl_lang/src/syntax/context.rs | 10 ++--- vhdl_lang/src/syntax/declarative_part.rs | 8 ++-- vhdl_lang/src/syntax/design_unit.rs | 40 ++++++++++---------- vhdl_ls/src/requests/document_symbol.rs | 22 +++++------ 9 files changed, 96 insertions(+), 78 deletions(-) diff --git a/vhdl_lang/src/analysis/concurrent.rs b/vhdl_lang/src/analysis/concurrent.rs index 36c9a7f8..af3c6e76 100644 --- a/vhdl_lang/src/analysis/concurrent.rs +++ b/vhdl_lang/src/analysis/concurrent.rs @@ -65,7 +65,7 @@ impl<'a> AnalyzeContext<'a> { sensitivity_list, decl, statements, - range: _, + source_range: _, } = process; if let Some(sensitivity_list) = sensitivity_list { match sensitivity_list { diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index 049fde90..14a76112 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -873,7 +873,9 @@ pub struct BlockStatement { pub header: BlockHeader, pub decl: Vec, pub statements: Vec, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 11.2 Block statement @@ -898,7 +900,9 @@ pub struct ProcessStatement { pub sensitivity_list: Option, pub decl: Vec, pub statements: Vec, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 11.4 Concurrent procedure call statements @@ -1013,7 +1017,9 @@ pub enum ContextItem { pub struct ContextDeclaration { pub ident: Ident, pub items: ContextClause, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 4.9 Package instatiation declaration @@ -1023,7 +1029,9 @@ pub struct PackageInstantiation { pub ident: Ident, pub package_name: WithPos, pub generic_map: Option>, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 7.3 Configuration specification @@ -1111,7 +1119,9 @@ pub struct ConfigurationDeclaration { pub decl: Vec, pub vunit_bind_inds: Vec, pub block_config: BlockConfiguration, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 3.2 Entity declarations @@ -1123,7 +1133,9 @@ pub struct EntityDeclaration { pub port_clause: Option>>, pub decl: WithPos>, pub statements: Option>>, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 3.3 Architecture bodies @@ -1134,7 +1146,9 @@ pub struct ArchitectureBody { pub entity_name: WithRef, pub decl: WithPos>, pub statements: WithPos>, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 4.7 Package declarations @@ -1144,7 +1158,9 @@ pub struct PackageDeclaration { pub ident: Ident, pub generic_clause: Option>>, pub decl: WithPos>, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 4.8 Package bodies @@ -1153,7 +1169,9 @@ pub struct PackageBody { pub context_clause: ContextClause, pub ident: WithRef, pub decl: WithPos>, - pub range: SrcPos, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 13.1 Design units diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index 9d1bcd7b..b3efc7ad 100644 --- a/vhdl_lang/src/ast/search.rs +++ b/vhdl_lang/src/ast/search.rs @@ -430,7 +430,7 @@ impl Search for LabeledConcurrentStatement { sensitivity_list, decl, statements, - range: _, + source_range: _, } = process; return_if_found!(sensitivity_list.search(searcher)); return_if_found!(decl.search(searcher)); diff --git a/vhdl_lang/src/syntax/concurrent_statement.rs b/vhdl_lang/src/syntax/concurrent_statement.rs index 9c78a2e2..0b101a6a 100644 --- a/vhdl_lang/src/syntax/concurrent_statement.rs +++ b/vhdl_lang/src/syntax/concurrent_statement.rs @@ -55,7 +55,7 @@ pub fn parse_block_statement( header, decl, statements, - range: start_pos.combine_into(&semi_token.pos), + source_range: start_pos.combine_into(&semi_token.pos), }) } @@ -216,7 +216,7 @@ pub fn parse_process_statement( sensitivity_list, decl, statements, - range: start_pos.combine_into(&semi_token.pos), + source_range: start_pos.combine_into(&semi_token.pos), }) } @@ -806,7 +806,7 @@ end block; label: Some(code.s1("name2").ident()), statement: ConcurrentStatement::ProcedureCall(call), }], - range: source_range(&code, "block", "end block;"), + source_range: source_range(&code, "block", "end block;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -832,7 +832,7 @@ end block name; }, decl: vec![], statements: vec![], - range: source_range(&code, "block", "end block name;"), + source_range: source_range(&code, "block", "end block name;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -858,7 +858,7 @@ end block; }, decl: vec![], statements: vec![], - range: source_range(&code, "block", "end block;"), + source_range: source_range(&code, "block", "end block;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -884,7 +884,7 @@ end block; }, decl: vec![], statements: vec![], - range: source_range(&code, "block", "end block;"), + source_range: source_range(&code, "block", "end block;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -914,7 +914,7 @@ end block; }, decl: vec![], statements: vec![], - range: source_range(&code, "block", "end block;"), + source_range: source_range(&code, "block", "end block;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -935,7 +935,7 @@ end process; sensitivity_list: None, decl: vec![], statements: vec![], - range: source_range(&code, "process", "end process;"), + source_range: source_range(&code, "process", "end process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -956,7 +956,7 @@ end process name; sensitivity_list: None, decl: vec![], statements: vec![], - range: source_range(&code, "process", "end process name;"), + source_range: source_range(&code, "process", "end process name;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -977,7 +977,7 @@ end process; sensitivity_list: None, decl: vec![], statements: vec![], - range: source_range(&code, "postponed", "end process;"), + source_range: source_range(&code, "postponed", "end process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -998,7 +998,7 @@ end postponed process; sensitivity_list: None, decl: vec![], statements: vec![], - range: source_range(&code, "postponed", "process;"), + source_range: source_range(&code, "postponed", "process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -1020,7 +1020,7 @@ end postponed process; sensitivity_list: None, decl: Vec::new(), statements: Vec::new(), - range: source_range(&code, "process", "process;"), + source_range: source_range(&code, "process", "process;"), }; assert_eq!( diagnostics, @@ -1049,7 +1049,7 @@ end process; ])), decl: vec![], statements: vec![], - range: source_range(&code, "process", "end process;"), + source_range: source_range(&code, "process", "end process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -1071,7 +1071,7 @@ end process; sensitivity_list: Some(SensitivityList::Names(Vec::new())), decl: Vec::new(), statements: Vec::new(), - range: source_range(&code, "process", "end process;"), + source_range: source_range(&code, "process", "end process;"), }; assert_eq!( diagnostics, @@ -1103,7 +1103,7 @@ end process; code.s1("foo <= true;").sequential_statement(), code.s1("wait;").sequential_statement(), ], - range: source_range(&code, "process", "end process;"), + source_range: source_range(&code, "process", "end process;"), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); diff --git a/vhdl_lang/src/syntax/configuration.rs b/vhdl_lang/src/syntax/configuration.rs index c3cf329a..bd3dd280 100644 --- a/vhdl_lang/src/syntax/configuration.rs +++ b/vhdl_lang/src/syntax/configuration.rs @@ -302,7 +302,7 @@ pub fn parse_configuration_declaration( diagnostics.push(diagnostic) } let semi_token = stream.expect_kind(SemiColon)?; - let range = configuration_token.pos.combine_into(&semi_token); + let source_range = configuration_token.pos.combine_into(&semi_token); Ok(ConfigurationDeclaration { context_clause: ContextClause::default(), ident, @@ -310,7 +310,7 @@ pub fn parse_configuration_declaration( decl, vunit_bind_inds, block_config, - range, + source_range, }) } @@ -378,7 +378,7 @@ end; use_clauses: vec![], items: vec![], }, - range: source_range(&code, "configuration", "end;") + source_range: source_range(&code, "configuration", "end;") } ); } @@ -406,7 +406,7 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -438,7 +438,7 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -472,7 +472,7 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -500,7 +500,7 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -543,7 +543,7 @@ end configuration cfg; }) ], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -589,7 +589,7 @@ end configuration cfg; }), }),], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -646,7 +646,7 @@ end configuration cfg; }), }),], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -694,7 +694,7 @@ end configuration cfg; block_config: None, }),], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -773,7 +773,7 @@ end configuration cfg; }) ], }, - range: source_range(&code, "configuration cfg", "end configuration cfg;"), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } diff --git a/vhdl_lang/src/syntax/context.rs b/vhdl_lang/src/syntax/context.rs index 8fb70f68..829a9736 100644 --- a/vhdl_lang/src/syntax/context.rs +++ b/vhdl_lang/src/syntax/context.rs @@ -114,7 +114,7 @@ pub fn parse_context( let ident = to_simple_name(name)?; diagnostics.push_some(error_on_end_identifier_mismatch(&ident, &end_ident)); - let range = match semi_pos { + let source_range = match semi_pos { Some(semi) => context_token.pos.combine_into(&semi), None => match stream.peek_expect() { Ok(token) => context_token.pos.combine_into(&token.pos), @@ -124,7 +124,7 @@ pub fn parse_context( Ok(DeclarationOrReference::Declaration(ContextDeclaration { ident, items, - range, + source_range, })) } else { // Context reference @@ -261,7 +261,7 @@ end context ident; DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), items: vec![], - range: source_range(&code, "context", ";"), + source_range: source_range(&code, "context", ";"), }) ); } @@ -288,7 +288,7 @@ end context ident2; DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), items: vec![], - range: source_range(&code, "context ident", "end context ident2;"), + source_range: source_range(&code, "context ident", "end context ident2;"), }) ); } @@ -328,7 +328,7 @@ end context; code.s1("context foo.ctx;") ), ], - range: source_range(&code, "context ident", "end context;"), + source_range: source_range(&code, "context ident", "end context;"), }) ) } diff --git a/vhdl_lang/src/syntax/declarative_part.rs b/vhdl_lang/src/syntax/declarative_part.rs index d81d0162..fcb6dbd8 100644 --- a/vhdl_lang/src/syntax/declarative_part.rs +++ b/vhdl_lang/src/syntax/declarative_part.rs @@ -35,13 +35,13 @@ pub fn parse_package_instantiation(stream: &mut TokenStream) -> ParseResult (None, token)); - let range = package_token.pos.combine_into(&semi_token); + let source_range = package_token.pos.combine_into(&semi_token); Ok(PackageInstantiation { context_clause: ContextClause::default(), ident, package_name, generic_map, - range, + source_range, }) } @@ -222,7 +222,7 @@ package ident is new lib.foo.bar; ident: code.s1("ident").ident(), package_name: code.s1("lib.foo.bar").selected_name(), generic_map: None, - range: source_range(&code, "package", ";"), + source_range: source_range(&code, "package", ";"), } ); } @@ -249,7 +249,7 @@ package ident is new lib.foo.bar )") .association_list() ), - range: source_range(&code, "package", ");"), + source_range: source_range(&code, "package", ");"), } ); } diff --git a/vhdl_lang/src/syntax/design_unit.rs b/vhdl_lang/src/syntax/design_unit.rs index baa8dee8..657dbd7a 100644 --- a/vhdl_lang/src/syntax/design_unit.rs +++ b/vhdl_lang/src/syntax/design_unit.rs @@ -66,7 +66,7 @@ pub fn parse_entity_declaration( diagnostics.push(diagnostic); } let semi_token = stream.expect_kind(SemiColon)?; - let range = entity_token.pos.combine_into(&semi_token); + let source_range = entity_token.pos.combine_into(&semi_token); Ok(EntityDeclaration { context_clause: ContextClause::default(), @@ -75,7 +75,7 @@ pub fn parse_entity_declaration( port_clause, decl, statements, - range, + source_range, }) } @@ -108,7 +108,7 @@ pub fn parse_architecture_body( } let semi_token = stream.expect_kind(SemiColon)?; - let range = architecture_token.pos.combine_into(&semi_token); + let source_range = architecture_token.pos.combine_into(&semi_token); Ok(ArchitectureBody { context_clause: ContextClause::default(), ident, @@ -118,7 +118,7 @@ pub fn parse_architecture_body( item: statements, pos: statements_start.combine_into_between(&end_token.pos), }, - range, + source_range, }) } @@ -152,13 +152,13 @@ pub fn parse_package_declaration( } stream.pop_if_kind(Identifier)?; let semi_token = stream.expect_kind(SemiColon)?; - let range = package_token.pos.combine_into(&semi_token); + let source_range = package_token.pos.combine_into(&semi_token); Ok(PackageDeclaration { context_clause: ContextClause::default(), ident, generic_clause, decl, - range, + source_range, }) } @@ -181,12 +181,12 @@ pub fn parse_package_body( diagnostics.push(diagnostic); } let semi_token = stream.expect_kind(SemiColon)?; - let range = package_token.pos.combine_into(&semi_token); + let source_range = package_token.pos.combine_into(&semi_token); Ok(PackageBody { context_clause: ContextClause::default(), ident: ident.into_ref(), decl, - range, + source_range, }) } @@ -360,7 +360,7 @@ mod tests { pos: source_range_between(&code, &format!("entity {} is", &ident), "end"), }, statements: None, - range: source_range(code, &format!("entity {}", &ident), ";"), + source_range: source_range(code, &format!("entity {}", &ident), ";"), })) } @@ -407,7 +407,7 @@ end entity; pos: source_range_between(&code, ");", "end"), }, statements: None, - range: source_range(&code, "entity", "end entity;"), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -441,7 +441,7 @@ end entity; pos: source_range_between(&code, ");", "end"), }, statements: None, - range: source_range(&code, "entity", "end entity;"), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -470,7 +470,7 @@ end entity; pos: source_range_between(&code, ");", "end"), }, statements: None, - range: source_range(&code, "entity", "end entity;"), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -499,7 +499,7 @@ end entity; item: vec![], pos: source_range_between(&code, "begin", "end"), }), - range: source_range(&code, "entity", "end entity;"), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -525,7 +525,7 @@ end entity; pos: source_range_between(&code, "myent is", "end entity"), }, statements: None, - range: source_range(&code, "entity", "end entity;"), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -555,7 +555,7 @@ end entity; item: vec![code.s1("check(clk, valid);").concurrent_statement()], pos: source_range_between(&code, "begin", "end") }), - range: source_range(&code, "entity", "end entity;"), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -602,7 +602,7 @@ end; item: vec![], pos: source_range_between(code, "begin", "end"), }, - range: source_range(code, &format!("architecture {}", &ident), ";"), + source_range: source_range(code, &format!("architecture {}", &ident), ";"), })) } @@ -669,7 +669,7 @@ end package; item: vec![], pos: source_range_between(&code, "is", "end package;"), }, - range: source_range(&code, "package", "end package;"), + source_range: source_range(&code, "package", "end package;"), } ); } @@ -699,7 +699,7 @@ end package; .declarative_part(), pos: source_range_between(&code, "is", "end package;") }, - range: source_range(&code, "package", "end package;"), + source_range: source_range(&code, "package", "end package;"), } ); } @@ -729,7 +729,7 @@ end package; item: vec![], pos: source_range_between(&code, ");", "end package;"), }, - range: source_range(&code, "package", "end package;"), + source_range: source_range(&code, "package", "end package;"), } ); } @@ -766,7 +766,7 @@ end entity; pos: source_range_between(&code, "is", "end"), }, statements: None, - range: source_range(&code, "entity", "end entity;"), + source_range: source_range(&code, "entity", "end entity;"), } ))] } diff --git a/vhdl_ls/src/requests/document_symbol.rs b/vhdl_ls/src/requests/document_symbol.rs index 5a64444d..e802170e 100644 --- a/vhdl_ls/src/requests/document_symbol.rs +++ b/vhdl_ls/src/requests/document_symbol.rs @@ -78,7 +78,7 @@ impl HasDocumentSymbol for EntityDeclaration { if let Some(ref statements) = self.statements { push_concurrent_statements(statements, &mut children); } - let mut range = to_lsp_range(&self.range); + let mut range = to_lsp_range(&self.source_range); if !self.context_clause.is_empty() { range.start = to_lsp_pos(self.context_clause[0].pos.start()); } @@ -98,7 +98,7 @@ impl HasDocumentSymbol for ConfigurationDeclaration { fn document_symbol(&self) -> DocumentSymbol { let mut children = vec![]; push_context_clause(&self.context_clause, &mut children); - let mut range = to_lsp_range(&self.range); + let mut range = to_lsp_range(&self.source_range); if !self.context_clause.is_empty() { range.start = to_lsp_pos(self.context_clause[0].pos.start()); } @@ -124,7 +124,7 @@ impl HasDocumentSymbol for PackageDeclaration { &mut children, ); push_declarations(&self.decl, &mut children); - let mut range = to_lsp_range(&self.range); + let mut range = to_lsp_range(&self.source_range); if !self.context_clause.is_empty() { range.start = to_lsp_pos(self.context_clause[0].pos.start()); } @@ -144,7 +144,7 @@ impl HasDocumentSymbol for PackageInstantiation { fn document_symbol(&self) -> DocumentSymbol { let mut children = vec![]; push_context_clause(&self.context_clause, &mut children); - let mut range = to_lsp_range(&self.range); + let mut range = to_lsp_range(&self.source_range); if !self.context_clause.is_empty() { range.start = to_lsp_pos(self.context_clause[0].pos.start()); } @@ -169,7 +169,7 @@ impl HasDocumentSymbol for ContextDeclaration { detail: Some(String::from("context")), kind: SymbolKind::Namespace, deprecated: None, - range: to_lsp_range(&self.range), + range: to_lsp_range(&self.source_range), selection_range: to_lsp_range(&self.ident.pos), children: none_if_empty(children), } @@ -190,7 +190,7 @@ impl HasDocumentSymbol for PackageBody { let mut children = vec![]; push_context_clause(&self.context_clause, &mut children); push_declarations(&self.decl, &mut children); - let mut range = to_lsp_range(&self.range); + let mut range = to_lsp_range(&self.source_range); if !self.context_clause.is_empty() { range.start = to_lsp_pos(self.context_clause[0].pos.start()); } @@ -209,7 +209,7 @@ impl HasDocumentSymbol for PackageBody { impl HasDocumentSymbol for ArchitectureBody { fn document_symbol(&self) -> DocumentSymbol { let mut children = vec![]; - let mut range = to_lsp_range(&self.range); + let mut range = to_lsp_range(&self.source_range); if !self.context_clause.is_empty() { range.start = to_lsp_pos(self.context_clause[0].pos.start()); } @@ -550,13 +550,13 @@ impl HasDocumentSymbol for ConcurrentProcedureCall { // TODO: Add children from generics, ports, declarations and statements impl HasDocumentSymbol for BlockStatement { fn document_symbol(&self) -> DocumentSymbol { - let block_start = to_lsp_pos(self.range.start()); + let block_start = to_lsp_pos(self.source_range.start()); DocumentSymbol { name: String::from(" "), detail: Some(String::from("block")), kind: SymbolKind::Namespace, deprecated: None, - range: to_lsp_range(&self.range), + range: to_lsp_range(&self.source_range), selection_range: lsp_types::Range { start: block_start, end: block_start, @@ -568,13 +568,13 @@ impl HasDocumentSymbol for BlockStatement { impl HasDocumentSymbol for ProcessStatement { fn document_symbol(&self) -> DocumentSymbol { - let start_pos = to_lsp_pos(self.range.start()); + let start_pos = to_lsp_pos(self.source_range.start()); DocumentSymbol { name: String::from(" "), detail: Some(String::from("process")), kind: SymbolKind::Event, deprecated: None, - range: to_lsp_range(&self.range), + range: to_lsp_range(&self.source_range), selection_range: lsp_types::Range { start: start_pos, end: start_pos, From 4f23ae17f77daa27529465746fe9d4ec3cb8e86f Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Mon, 26 Jul 2021 13:07:30 +0200 Subject: [PATCH 10/10] Fix clippy --- vhdl_ls/src/requests/document_symbol.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/vhdl_ls/src/requests/document_symbol.rs b/vhdl_ls/src/requests/document_symbol.rs index e802170e..26975858 100644 --- a/vhdl_ls/src/requests/document_symbol.rs +++ b/vhdl_ls/src/requests/document_symbol.rs @@ -934,7 +934,7 @@ mod tests { for err in diagnostics.iter() { println!("{}", err.show()); } - if diagnostics.len() > 0 { + if !diagnostics.is_empty() { panic!("Found errors"); } design_file @@ -969,16 +969,15 @@ mod tests { let mut found_start = false; let mut end_line = 0; let mut end_column = 0; - let mut line_number = 0; let mut occurance = 0; - for line in code.lines() { + for (line_number, line) in code.lines().enumerate() { if !found_start { if let Some(pos) = line.find(start) { occurance += 1; if occurance == start_occurance { start_column = pos; if !inclusive { - start_column = start_column + start.len(); + start_column += start.len(); } start_line = line_number; found_start = true; @@ -989,13 +988,12 @@ mod tests { if let Some(pos) = line.find(end) { end_column = pos; if inclusive { - end_column = end_column + end.len(); + end_column += end.len(); } end_line = line_number; break; } } - line_number += 1; } lsp_types::Range { @@ -1347,7 +1345,7 @@ end architecture; ) -> DocumentSymbol { DocumentSymbol { name: String::from(name), - detail: detail.map(|detail| String::from(detail)), + detail: detail.map(String::from), kind, deprecated: None, range: range1(code, name), @@ -1365,7 +1363,7 @@ end architecture; ) -> DocumentSymbol { DocumentSymbol { name: String::from(name), - detail: detail.map(|detail| String::from(detail)), + detail: detail.map(String::from), kind, deprecated: None, range, @@ -1783,7 +1781,7 @@ end; fn statement(code: &str, name: &str, detail: Option<&str>, kind: SymbolKind) -> DocumentSymbol { DocumentSymbol { name: String::from(name), - detail: detail.map(|detail| String::from(detail)), + detail: detail.map(String::from), kind, deprecated: None, range: range1(code, name), @@ -1801,7 +1799,7 @@ end; ) -> DocumentSymbol { DocumentSymbol { name: String::from(name), - detail: detail.map(|detail| String::from(detail)), + detail: detail.map(String::from), kind, deprecated: None, range,