From a99a0b322a0d82b38632cd6334fcdb027baae9f1 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sat, 25 Apr 2020 22:16:57 +0200 Subject: [PATCH 01/14] Add source range to design units --- vhdl_lang/src/ast.rs | 18 ++ vhdl_lang/src/syntax/configuration.rs | 38 +-- vhdl_lang/src/syntax/declarative_part.rs | 22 +- vhdl_lang/src/syntax/design_unit.rs | 285 +++++++++++++++++++++-- vhdl_lang/src/syntax/test.rs | 12 + 5 files changed, 332 insertions(+), 43 deletions(-) diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index 034b1df2..997e32d1 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -1006,6 +1006,9 @@ pub struct PackageInstantiation { pub ident: Ident, pub package_name: WithPos, pub generic_map: Option>, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 7.3 Configuration specification @@ -1093,6 +1096,9 @@ pub struct ConfigurationDeclaration { pub decl: Vec, pub vunit_bind_inds: Vec, pub block_config: BlockConfiguration, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 3.2 Entity declarations @@ -1104,6 +1110,9 @@ pub struct EntityDeclaration { pub port_clause: Option>, pub decl: Vec, pub statements: Vec, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 3.3 Architecture bodies #[derive(PartialEq, Debug, Clone)] @@ -1113,6 +1122,9 @@ pub struct ArchitectureBody { pub entity_name: WithRef, pub decl: Vec, pub statements: Vec, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 4.7 Package declarations @@ -1122,6 +1134,9 @@ pub struct PackageDeclaration { pub ident: Ident, pub generic_clause: Option>, pub decl: Vec, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 4.8 Package bodies @@ -1130,6 +1145,9 @@ pub struct PackageBody { pub context_clause: ContextClause, pub ident: WithRef, pub decl: Vec, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 13.1 Design units diff --git a/vhdl_lang/src/syntax/configuration.rs b/vhdl_lang/src/syntax/configuration.rs index 5e9413d4..9df38f6f 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,7 @@ 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)?; Ok(ConfigurationDeclaration { context_clause: ContextClause::default(), ident, @@ -309,6 +309,7 @@ pub fn parse_configuration_declaration( decl, vunit_bind_inds, block_config, + source_range: configuration_token.pos.combine_into(&semi_token), }) } @@ -351,7 +352,8 @@ pub fn parse_configuration_specification( #[cfg(test)] mod tests { use super::*; - use crate::syntax::test::Code; + use crate::syntax::test::{source_range, Code}; + use pretty_assertions::assert_eq; #[test] fn empty_configuration() { @@ -375,7 +377,8 @@ end; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + source_range: source_range(&code, (0, 0), (3, 4)) } ); } @@ -402,7 +405,8 @@ end configuration cfg; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + source_range: source_range(&code, (0, 0), (3, 22)), } ); } @@ -433,7 +437,8 @@ end configuration cfg; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + source_range: source_range(&code, (0, 0), (5, 22)), } ); } @@ -466,7 +471,8 @@ end configuration cfg; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + source_range: source_range(&code, (0, 0), (5, 22)), } ); } @@ -493,7 +499,8 @@ end configuration cfg; block_spec: code.s1("rtl(0)").name(), use_clauses: vec![], items: vec![], - } + }, + source_range: source_range(&code, (0, 0), (3, 22)), } ); } @@ -535,7 +542,8 @@ end configuration cfg; items: vec![], }) ], - } + }, + source_range: source_range(&code, (0, 0), (7, 22)), } ); } @@ -580,7 +588,8 @@ end configuration cfg; items: vec![], }), }),], - } + }, + source_range: source_range(&code, (0, 0), (7, 22)), } ); } @@ -636,7 +645,8 @@ end configuration cfg; items: vec![], }), }),], - } + }, + source_range: source_range(&code, (0, 0), (9, 22)), } ); } @@ -683,7 +693,8 @@ end configuration cfg; vunit_bind_inds: Vec::new(), block_config: None, }),], - } + }, + source_range: source_range(&code, (0, 0), (6, 22)), } ); } @@ -761,7 +772,8 @@ end configuration cfg; block_config: None, }) ], - } + }, + source_range: source_range(&code, (0, 0), (11, 22)), } ); } diff --git a/vhdl_lang/src/syntax/declarative_part.rs b/vhdl_lang/src/syntax/declarative_part.rs index 8005fb6e..20d358bc 100644 --- a/vhdl_lang/src/syntax/declarative_part.rs +++ b/vhdl_lang/src/syntax/declarative_part.rs @@ -19,27 +19,31 @@ use crate::ast::{ContextClause, Declaration, PackageInstantiation}; use crate::data::DiagnosticHandler; 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) + } + ); Ok(PackageInstantiation { context_clause: ContextClause::default(), ident, package_name, generic_map, + source_range: package_token.pos.combine_into(&semi_token), }) } @@ -162,7 +166,7 @@ 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() { @@ -177,7 +181,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, + source_range: source_range(&code, (0, 0), (0, 33)), } ); } @@ -203,7 +208,8 @@ package ident is new lib.foo.bar foo => bar )") .association_list() - ) + ), + source_range: source_range(&code, (0, 0), (3, 4)), } ); } diff --git a/vhdl_lang/src/syntax/design_unit.rs b/vhdl_lang/src/syntax/design_unit.rs index 9c003434..17ce9817 100644 --- a/vhdl_lang/src/syntax/design_unit.rs +++ b/vhdl_lang/src/syntax/design_unit.rs @@ -27,7 +27,7 @@ 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)?; @@ -48,7 +48,7 @@ pub fn parse_entity_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)?; Ok(EntityDeclaration { context_clause: ContextClause::default(), ident, @@ -56,6 +56,7 @@ pub fn parse_entity_declaration( port_clause, decl, statements, + source_range: entity_token.pos.combine_into(&semi_token), }) } @@ -64,7 +65,7 @@ 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()?; @@ -80,7 +81,7 @@ pub fn parse_architecture_body( diagnostics.push(diagnostic); } - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; Ok(ArchitectureBody { context_clause: ContextClause::default(), @@ -88,6 +89,7 @@ pub fn parse_architecture_body( entity_name: entity_name.into_ref(), decl, statements, + source_range: architecture_token.pos.combine_into(&semi_token), }) } @@ -96,7 +98,7 @@ 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)?; @@ -116,12 +118,13 @@ pub fn parse_package_declaration( diagnostics.push(diagnostic); } stream.pop_if_kind(Identifier)?; - stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?; Ok(PackageDeclaration { context_clause: ContextClause::default(), ident, generic_clause, decl, + source_range: package_token.pos.combine_into(&semi_token), }) } @@ -130,7 +133,7 @@ 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()?; @@ -143,12 +146,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)?; Ok(PackageBody { context_clause: ContextClause::default(), ident: ident.into_ref(), decl, + source_range: package_token.pos.combine_into(&semi_token), }) } @@ -214,6 +218,9 @@ pub fn parse_design_file( }, Entity => match parse_entity_declaration(stream, diagnostics) { Ok(mut entity) => { + if let Some(ref context_item) = context_clause.first() { + entity.source_range = entity.source_range.combine_into(context_item) + } entity.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Primary(AnyPrimaryUnit::Entity(entity))); } @@ -222,6 +229,9 @@ pub fn parse_design_file( Architecture => match parse_architecture_body(stream, diagnostics) { Ok(mut architecture) => { + if let Some(ref context_item) = context_clause.first() { + architecture.source_range = architecture.source_range.combine_into(context_item) + } architecture.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Secondary(AnySecondaryUnit::Architecture(architecture))); } @@ -230,6 +240,9 @@ pub fn parse_design_file( Configuration => match parse_configuration_declaration(stream, diagnostics) { Ok(mut configuration) => { + if let Some(ref context_item) = context_clause.first() { + configuration.source_range = configuration.source_range.combine_into(context_item) + } configuration.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Primary(AnyPrimaryUnit::Configuration(configuration))); } @@ -239,6 +252,9 @@ pub fn parse_design_file( if stream.next_kinds_are(&[Package, Body])? { match parse_package_body(stream, diagnostics) { Ok(mut package_body) => { + if let Some(ref context_item) = context_clause.first() { + package_body.source_range = package_body.source_range.combine_into(context_item) + } package_body.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Secondary(AnySecondaryUnit::PackageBody(package_body))); } @@ -247,6 +263,9 @@ pub fn parse_design_file( } else if stream.next_kinds_are(&[Package, Identifier, Is, New])? { match parse_package_instantiation(stream) { Ok(mut inst) => { + if let Some(ref context_item) = context_clause.first() { + inst.source_range = inst.source_range.combine_into(context_item) + } inst.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Primary(AnyPrimaryUnit::PackageInstance(inst))) }, @@ -255,6 +274,9 @@ pub fn parse_design_file( } else { match parse_package_declaration(stream, diagnostics) { Ok(mut package) => { + if let Some(ref context_item) = context_clause.first() { + package.source_range = package.source_range.combine_into(context_item) + } package.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Primary(AnyPrimaryUnit::Package(package))) } @@ -280,7 +302,8 @@ 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, Code}; + use pretty_assertions::assert_eq; fn parse_str(code: &str) -> (Code, DesignFile, Vec) { let code = Code::new(code); @@ -309,7 +332,7 @@ mod tests { } /// An simple entity with only a name - fn simple_entity(ident: Ident) -> AnyDesignUnit { + fn simple_entity(ident: Ident, source_range: SrcPos) -> AnyDesignUnit { AnyDesignUnit::Primary(AnyPrimaryUnit::Entity(EntityDeclaration { context_clause: ContextClause::default(), ident, @@ -317,6 +340,7 @@ mod tests { port_clause: None, decl: vec![], statements: vec![], + source_range: source_range, })) } @@ -330,7 +354,10 @@ end entity; ); assert_eq!( design_file.design_units, - [simple_entity(code.s1("myent").ident())] + [simple_entity( + code.s1("myent").ident(), + source_range(&code, (1, 0), (2, 11)) + )] ); let (code, design_file) = parse_ok( @@ -341,7 +368,10 @@ end entity myent; ); assert_eq!( design_file.design_units, - [simple_entity(code.s1("myent").ident())] + [simple_entity( + code.s1("myent").ident(), + source_range(&code, (1, 0), (2, 17)) + )] ); } @@ -363,6 +393,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![], + source_range: source_range(&code, (1, 0), (3, 11)), } ); } @@ -390,6 +421,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![], + source_range: source_range(&code, (1, 0), (4, 11)), } ); } @@ -412,6 +444,7 @@ end entity; port_clause: Some(vec![]), decl: vec![], statements: vec![], + source_range: source_range(&code, (1, 0), (3, 11)), } ); } @@ -434,6 +467,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![], + source_range: source_range(&code, (1, 0), (3, 11)), } ); } @@ -456,6 +490,7 @@ end entity; port_clause: None, decl: code.s1("constant foo : natural := 0;").declarative_part(), statements: vec![], + source_range: source_range(&code, (1, 0), (3, 11)), } ); } @@ -479,6 +514,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![code.s1("check(clk, valid);").concurrent_statement()], + source_range: source_range(&code, (1, 0), (4, 11)), } ); } @@ -503,22 +539,39 @@ 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.s1("myent").ident(), + source_range(&code, (1, 0), (2, 11)) + ), + simple_entity( + code.s1("myent2").ident(), + source_range(&code, (4, 0), (5, 18)) + ), + simple_entity( + code.s1("myent3").ident(), + source_range(&code, (7, 0), (8, 11)) + ), + simple_entity( + code.s1("myent4").ident(), + source_range(&code, (10, 0), (11, 4)) + ) ] ); } // An simple entity with only a name - fn simple_architecture(ident: Ident, entity_name: Ident) -> AnyDesignUnit { + fn simple_architecture( + ident: Ident, + entity_name: Ident, + source_range: SrcPos, + ) -> AnyDesignUnit { AnyDesignUnit::Secondary(AnySecondaryUnit::Architecture(ArchitectureBody { context_clause: ContextClause::default(), ident, entity_name: entity_name.into_ref(), decl: Vec::new(), statements: vec![], + source_range: source_range, })) } @@ -535,7 +588,8 @@ end architecture; design_file.design_units, [simple_architecture( code.s1("arch_name").ident(), - code.s1("myent").ident() + code.s1("myent").ident(), + source_range(&code, (1, 0), (3, 17)) )] ); } @@ -553,7 +607,8 @@ end architecture arch_name; design_file.design_units, [simple_architecture( code.s1("arch_name").ident(), - code.s1("myent").ident() + code.s1("myent").ident(), + source_range(&code, (1, 0), (3, 27)) )] ); } @@ -571,7 +626,8 @@ end; design_file.design_units, [simple_architecture( code.s1("arch_name").ident(), - code.s1("myent").ident() + code.s1("myent").ident(), + source_range(&code, (1, 0), (3, 4)) )] ); } @@ -591,6 +647,7 @@ end package; ident: code.s1("pkg_name").ident(), generic_clause: None, decl: vec![], + source_range: source_range(&code, (1, 0), (2, 12)), } ); } @@ -617,6 +674,7 @@ end package; constant bar : natural := 0; ") .declarative_part(), + source_range: source_range(&code, (1, 0), (4, 12)), } ); } @@ -642,13 +700,14 @@ end package; code.s1("type foo").generic(), code.s1("type bar").generic() ]), - decl: vec![] + decl: vec![], + source_range: source_range(&code, (1, 0), (6, 12)), } ); } #[test] - fn context_clause_associated_with_design_units() { + fn context_clause_associated_with_entity_declaration() { let (code, design_file) = parse_ok( " library lib; @@ -676,12 +735,194 @@ end entity; port_clause: None, decl: vec![], statements: vec![], + source_range: source_range(&code, (1, 0), (5, 11)), } ))] } ); } + #[test] + fn context_clause_associated_with_architecture_body() { + let (code, design_file) = parse_ok( + " +library lib; +use lib.foo; + +architecture rtl of myent is +begin +end; + +", + ); + assert_eq!( + design_file, + DesignFile { + design_units: vec![AnyDesignUnit::Secondary(AnySecondaryUnit::Architecture( + ArchitectureBody { + context_clause: vec![ + code.s1("library lib;") + .library_clause() + .map_into(ContextItem::Library), + code.s1("use lib.foo;") + .use_clause() + .map_into(ContextItem::Use), + ], + ident: code.s1("rtl").ident(), + entity_name: code.s1("myent").ident().into_ref(), + decl: Vec::new(), + statements: vec![], + source_range: source_range(&code, (1, 0), (6, 4)), + } + ))], + } + ); + } + + #[test] + fn context_clause_associated_with_package_declaration() { + let (code, design_file) = parse_ok( + " +library lib; +use lib.foo; + +package pkg_name is +end; + +", + ); + assert_eq!( + design_file, + DesignFile { + design_units: vec![AnyDesignUnit::Primary(AnyPrimaryUnit::Package( + PackageDeclaration { + context_clause: vec![ + code.s1("library lib;") + .library_clause() + .map_into(ContextItem::Library), + code.s1("use lib.foo;") + .use_clause() + .map_into(ContextItem::Use), + ], + ident: code.s1("pkg_name").ident(), + generic_clause: None, + decl: vec![], + source_range: source_range(&code, (1, 0), (5, 4)), + } + ))], + } + ); + } + + #[test] + fn context_clause_associated_with_package_body() { + let (code, design_file) = parse_ok( + " +library lib; +use lib.foo; + +package body pkg_name is +end; + +", + ); + assert_eq!( + design_file, + DesignFile { + design_units: vec![AnyDesignUnit::Secondary(AnySecondaryUnit::PackageBody( + PackageBody { + context_clause: vec![ + code.s1("library lib;") + .library_clause() + .map_into(ContextItem::Library), + code.s1("use lib.foo;") + .use_clause() + .map_into(ContextItem::Use), + ], + ident: code.s1("pkg_name").ident().into_ref(), + decl: vec![], + source_range: source_range(&code, (1, 0), (5, 4)), + } + ))], + } + ); + } + + #[test] + fn context_clause_associated_with_configuration_declaration() { + let (code, design_file) = parse_ok( + " +library lib; +use lib.foo; + +configuration cfg of entity_name is + for rtl(0) + end for; +end; +", + ); + assert_eq!( + design_file, + DesignFile { + design_units: vec![AnyDesignUnit::Primary(AnyPrimaryUnit::Configuration( + ConfigurationDeclaration { + context_clause: vec![ + code.s1("library lib;") + .library_clause() + .map_into(ContextItem::Library), + code.s1("use lib.foo;") + .use_clause() + .map_into(ContextItem::Use), + ], + ident: code.s1("cfg").ident(), + entity_name: code.s1("entity_name").selected_name(), + decl: vec![], + vunit_bind_inds: Vec::new(), + block_config: BlockConfiguration { + block_spec: code.s1("rtl(0)").name(), + use_clauses: vec![], + items: vec![], + }, + source_range: source_range(&code, (1, 0), (7, 4)) + } + ))], + } + ); + } + + #[test] + fn context_clause_associated_with_package_instantiation() { + let (code, design_file) = parse_ok( + " +library lib; +use lib.foo; + +package ident is new lib.foo.bar; +", + ); + assert_eq!( + design_file, + DesignFile { + design_units: vec![AnyDesignUnit::Primary(AnyPrimaryUnit::PackageInstance( + PackageInstantiation { + context_clause: vec![ + code.s1("library lib;") + .library_clause() + .map_into(ContextItem::Library), + code.s1("use lib.foo;") + .use_clause() + .map_into(ContextItem::Use), + ], + ident: code.s1("ident").ident(), + package_name: code.s1("lib.foo.bar").selected_name(), + generic_map: None, + source_range: source_range(&code, (1, 0), (4, 33)), + } + ))], + } + ); + } + #[test] fn warning_on_orphan_context_clause() { let code = Code::new( diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index 0e501635..5a3ca367 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -569,6 +569,18 @@ impl AsRef for Code { } } +pub fn source_range(code: &Code, start: (u32, u32), end: (u32, u32)) -> SrcPos { + let (start_line, start_column) = start; + let (end_line, end_column) = end; + SrcPos::new( + code.source().clone(), + crate::data::Range::new( + Position::new(start_line, start_column), + Position::new(end_line, end_column), + ), + ) +} + mod tests { use super::*; From c4ca333d0b327408ae99b79b1679faf34b63a99e Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sat, 25 Apr 2020 23:01:51 +0200 Subject: [PATCH 02/14] Add source range to context declaration --- vhdl_lang/src/syntax/context.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/vhdl_lang/src/syntax/context.rs b/vhdl_lang/src/syntax/context.rs index b43dbe58..3b8ab001 100644 --- a/vhdl_lang/src/syntax/context.rs +++ b/vhdl_lang/src/syntax/context.rs @@ -104,12 +104,11 @@ pub fn parse_context( End => { stream.pop_if_kind(Context)?; end_ident = stream.pop_optional_ident()?; - stream.expect_kind(SemiColon)?; break; } ) } - + let semi_token = stream.expect_kind(SemiColon)?; let ident = to_simple_name(name)?; diagnostics.push_some(error_on_end_identifier_mismatch(&ident, &end_ident)); @@ -117,6 +116,7 @@ pub fn parse_context( Ok(DeclarationOrReference::Declaration(ContextDeclaration { ident, items, + source_range: context_token.pos.combine_into(&semi_token), })) } else { // Context reference @@ -139,8 +139,9 @@ pub fn parse_context( mod tests { use super::*; + use pretty_assertions::assert_eq; use crate::data::Diagnostic; - use crate::syntax::test::Code; + use crate::syntax::test::{source_range, Code}; #[test] fn test_library_clause_single_name() { @@ -246,13 +247,21 @@ context ident is end context ident; ", ]; - for variant in variants { + let source_ranges = vec![ + ((0, 0), (1, 4)), + ((0, 0), (1, 12)), + ((0, 0), (1, 10)), + ((0, 0), (1, 18)), + ]; + for variant in variants.iter().zip(source_ranges.iter()) { + let (variant, (start, end)) = variant; let code = Code::new(variant); assert_eq!( code.with_stream_no_diagnostics(parse_context), DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), - items: vec![] + items: vec![], + source_range: source_range(&code, *start, *end), }) ); } @@ -278,7 +287,8 @@ end context ident2; context, DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), - items: vec![] + items: vec![], + source_range: source_range(&code, (0, 0), (1, 19)), }) ); } @@ -317,7 +327,8 @@ end context; }), code.s1("context foo.ctx;") ), - ] + ], + source_range: source_range(&code, (0, 0), (4, 12)), }) ) } From bbb8d963f468194f562c42a08686eb5130b65d54 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 13:48:34 +0200 Subject: [PATCH 03/14] Add source range to ContextDeclaration --- vhdl_lang/src/ast.rs | 3 +++ vhdl_lang/src/syntax/context.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index 997e32d1..cd13ec8a 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -997,6 +997,9 @@ pub enum ContextItem { pub struct ContextDeclaration { pub ident: Ident, pub items: ContextClause, + + // Non-LRM fields + pub source_range: SrcPos, } /// LRM 4.9 Package instatiation declaration diff --git a/vhdl_lang/src/syntax/context.rs b/vhdl_lang/src/syntax/context.rs index 3b8ab001..2f429756 100644 --- a/vhdl_lang/src/syntax/context.rs +++ b/vhdl_lang/src/syntax/context.rs @@ -139,9 +139,9 @@ pub fn parse_context( mod tests { use super::*; - use pretty_assertions::assert_eq; use crate::data::Diagnostic; use crate::syntax::test::{source_range, Code}; + use pretty_assertions::assert_eq; #[test] fn test_library_clause_single_name() { From 0269d3aea41353eaafe10b9a05e282ba75d4f4b0 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 15:15:26 +0200 Subject: [PATCH 04/14] Add document_symbol --- vhdl_ls/src/document_symbol.rs | 519 +++++++++++++++++++++++++++++++++ vhdl_ls/src/lib.rs | 3 +- 2 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 vhdl_ls/src/document_symbol.rs diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs new file mode 100644 index 00000000..9838c162 --- /dev/null +++ b/vhdl_ls/src/document_symbol.rs @@ -0,0 +1,519 @@ +// 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) 2020, Olof Kraigher olof.kraigher@gmail.com + +use lsp_types::{DocumentSymbol, DocumentSymbolResponse, SymbolKind, Url}; +use vhdl_lang::ast::*; +use vhdl_lang::VHDLParser; + +pub fn nested_document_symbol_response_from_file(uri: &Url) -> Option { + if let Some(design_file) = parse_file(uri) { + Some(nested_document_symbol_response(&design_file)) + } else { + None + } +} + +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) +} + +fn symbol_kind(entity_class: EntityClass) -> SymbolKind { + match entity_class { + EntityClass::Entity => SymbolKind::Interface, + EntityClass::Architecture => SymbolKind::Class, + EntityClass::Configuration => SymbolKind::Constructor, + EntityClass::Package => SymbolKind::Package, + _ => SymbolKind::Unknown, + } +} + +fn symbol_kind_for_context() -> SymbolKind { + SymbolKind::Namespace +} + +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 { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("entity")), + kind: symbol_kind(EntityClass::Entity), + deprecated: None, + range: to_lsp_range(self.source_range.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } + } +} + +impl HasDocumentSymbol for ConfigurationDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("configuration")), + kind: symbol_kind(EntityClass::Configuration), + deprecated: None, + range: to_lsp_range(self.source_range.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } + } +} + +impl HasDocumentSymbol for PackageDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("package")), + kind: symbol_kind(EntityClass::Package), + deprecated: None, + range: to_lsp_range(self.source_range.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } + } +} + +impl HasDocumentSymbol for PackageInstantiation { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("package instance")), + kind: symbol_kind(EntityClass::Package), + deprecated: None, + range: to_lsp_range(self.source_range.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } + } +} + +impl HasDocumentSymbol for ContextDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("context")), + kind: symbol_kind_for_context(), + deprecated: None, + range: to_lsp_range(self.source_range.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } + } +} + +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 { + DocumentSymbol { + name: self.ident.item.item.name_utf8(), + detail: Some(String::from("package body")), + kind: symbol_kind(EntityClass::Package), + deprecated: None, + range: to_lsp_range(self.source_range.range()), + selection_range: to_lsp_range(self.ident.item.pos.range()), + children: None, + } + } +} + +impl HasDocumentSymbol for ArchitectureBody { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from(format!( + "architecture of {}", + self.entity_name.item.item.name().to_string() + ))), + kind: symbol_kind(EntityClass::Architecture), + deprecated: None, + range: to_lsp_range(self.source_range.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } + } +} + +fn parse_file(uri: &Url) -> Option { + match uri.to_file_path() { + Ok(url) => { + let mut diagnostics = vec![]; + match VHDLParser::default().parse_design_file(&url, &mut diagnostics) { + Ok((_, design_file)) => Some(design_file), + Err(_) => None, + } + } + _ => None, + } +} + +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(range: vhdl_lang::Range) -> lsp_types::Range { + lsp_types::Range { + start: to_lsp_pos(range.start), + end: to_lsp_pos(range.end), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use lsp_types; + use pretty_assertions::assert_eq; + 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 range(start: (u64, u64), end: (u64, u64)) -> lsp_types::Range { + let (start_line, start_character) = start; + let (end_line, end_character) = end; + lsp_types::Range { + start: lsp_types::Position::new(start_line, start_character), + end: lsp_types::Position::new(end_line, end_character), + } + } + + 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) + } + + #[test] + fn entity_declaration() { + let design_file = parse_str( + " +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity ent1 is +end; +", + ); + assert_eq!(design_file.design_units.len(), 1); + let entity = match design_file.design_units.first().unwrap() { + AnyDesignUnit::Primary(primary) => match primary { + AnyPrimaryUnit::Entity(entity) => entity, + _ => panic!("expected entity"), + }, + _ => panic!("expected entity"), + }; + + assert_eq!( + entity.document_symbol(), + DocumentSymbol { + name: String::from("ent1"), + detail: Some(String::from("entity")), + kind: SymbolKind::Interface, + deprecated: None, + range: range((1, 0), (6, 4)), + selection_range: range((5, 7), (5, 11)), + children: None, + } + ); + } + + #[test] + fn configuration_declaration() { + let design_file = parse_str( + " +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +configuration cfg of entity_name is + for rtl(0) + end for; +end; +", + ); + assert_eq!(design_file.design_units.len(), 1); + let configuration = match design_file.design_units.first().unwrap() { + AnyDesignUnit::Primary(primary) => match primary { + AnyPrimaryUnit::Configuration(configuration) => configuration, + _ => panic!("expected configuration declaration"), + }, + _ => panic!("expected configuration declaration"), + }; + + assert_eq!( + configuration.document_symbol(), + DocumentSymbol { + name: String::from("cfg"), + detail: Some(String::from("configuration")), + kind: SymbolKind::Constructor, + deprecated: None, + range: range((1, 0), (8, 4)), + selection_range: range((5, 14), (5, 17)), + children: None, + } + ); + } + + #[test] + fn package_declaration() { + let design_file = parse_str( + " +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package pkg is +end; +", + ); + assert_eq!(design_file.design_units.len(), 1); + let package = match design_file.design_units.first().unwrap() { + AnyDesignUnit::Primary(primary) => match primary { + AnyPrimaryUnit::Package(package) => package, + _ => panic!("expected package declaration"), + }, + _ => panic!("expected package declaration"), + }; + + assert_eq!( + package.document_symbol(), + DocumentSymbol { + name: String::from("pkg"), + detail: Some(String::from("package")), + kind: SymbolKind::Package, + deprecated: None, + range: range((1, 0), (6, 4)), + selection_range: range((5, 8), (5, 11)), + children: None, + } + ); + } + + #[test] + fn package_instantiation() { + let design_file = parse_str( + " +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package pkg_inst is new work.pkg + generic map( + gen => 1 + ); +", + ); + assert_eq!(design_file.design_units.len(), 1); + let package_instance = match design_file.design_units.first().unwrap() { + AnyDesignUnit::Primary(primary) => match primary { + AnyPrimaryUnit::PackageInstance(package_instance) => package_instance, + _ => panic!("expected package declaration"), + }, + _ => panic!("expected package declaration"), + }; + + assert_eq!( + package_instance.document_symbol(), + DocumentSymbol { + name: String::from("pkg_inst"), + detail: Some(String::from("package instance")), + kind: SymbolKind::Package, + deprecated: None, + range: range((1, 0), (8, 6)), + selection_range: range((5, 8), (5, 16)), + children: None, + } + ); + } + + #[test] + fn context_declaration() { + let design_file = parse_str( + " +context ctx is + library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; +end; +", + ); + assert_eq!(design_file.design_units.len(), 1); + let context = match design_file.design_units.first().unwrap() { + AnyDesignUnit::Primary(primary) => match primary { + AnyPrimaryUnit::Context(context) => context, + _ => panic!("expected package declaration"), + }, + _ => panic!("expected package declaration"), + }; + + assert_eq!( + context.document_symbol(), + DocumentSymbol { + name: String::from("ctx"), + detail: Some(String::from("context")), + kind: SymbolKind::Namespace, + deprecated: None, + range: range((1, 0), (5, 4)), + selection_range: range((1, 8), (1, 11)), + children: None, + } + ); + } + + #[test] + fn package_body() { + let design_file = parse_str( + " +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package body pkg is +end; +", + ); + assert_eq!(design_file.design_units.len(), 1); + let package_body = match design_file.design_units.first().unwrap() { + AnyDesignUnit::Secondary(secondary) => match secondary { + AnySecondaryUnit::PackageBody(package_body) => package_body, + _ => panic!("expected package declaration"), + }, + _ => panic!("expected package declaration"), + }; + + assert_eq!( + package_body.document_symbol(), + DocumentSymbol { + name: String::from("pkg"), + detail: Some(String::from("package body")), + kind: SymbolKind::Package, + deprecated: None, + range: range((1, 0), (6, 4)), + selection_range: range((5, 13), (5, 16)), + children: None, + } + ); + } + + #[test] + fn architecture_body() { + let design_file = parse_str( + " +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +architecture rtl of ent is +begin +end; +", + ); + assert_eq!(design_file.design_units.len(), 1); + let architecture = match design_file.design_units.first().unwrap() { + AnyDesignUnit::Secondary(secondary) => match secondary { + AnySecondaryUnit::Architecture(architecture) => architecture, + _ => panic!("expected package declaration"), + }, + _ => panic!("expected package declaration"), + }; + + assert_eq!( + architecture.document_symbol(), + DocumentSymbol { + name: String::from("rtl"), + detail: Some(String::from("architecture of ent")), + kind: SymbolKind::Class, + deprecated: None, + range: range((1, 0), (7, 4)), + selection_range: range((5, 13), (5, 16)), + children: None, + } + ); + } + + #[test] + fn test_nested_document_symbol_response_from_file() { + let (source_url, _file) = write_source_file( + " +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity ent1 is +end; +", + ); + let response = nested_document_symbol_response_from_file(&source_url).unwrap(); + assert_eq!( + response, + DocumentSymbolResponse::from(vec![ + DocumentSymbol { + name: String::from("ent1"), + detail: Some(String::from("entity")), + kind: SymbolKind::Interface, + deprecated: None, + range: range((1, 0), (6, 4)), + selection_range: range((5, 7), (5, 11)), + children: None, + } + ]) + ); + } +} diff --git a/vhdl_ls/src/lib.rs b/vhdl_ls/src/lib.rs index 9284f75f..ca483f0f 100644 --- a/vhdl_ls/src/lib.rs +++ b/vhdl_ls/src/lib.rs @@ -2,11 +2,12 @@ // 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) 2020, Olof Kraigher olof.kraigher@gmail.com #[macro_use] extern crate log; +mod document_symbol; mod rpc_channel; mod stdio_server; mod vhdl_server; From 2e9bfab5548ab08b395fe4ec2b43174e34f01a83 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 15:38:02 +0200 Subject: [PATCH 05/14] clippy --- vhdl_lang/src/syntax/design_unit.rs | 4 ++-- vhdl_ls/src/document_symbol.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vhdl_lang/src/syntax/design_unit.rs b/vhdl_lang/src/syntax/design_unit.rs index 17ce9817..9caf1a79 100644 --- a/vhdl_lang/src/syntax/design_unit.rs +++ b/vhdl_lang/src/syntax/design_unit.rs @@ -340,7 +340,7 @@ mod tests { port_clause: None, decl: vec![], statements: vec![], - source_range: source_range, + source_range, })) } @@ -571,7 +571,7 @@ end; entity_name: entity_name.into_ref(), decl: Vec::new(), statements: vec![], - source_range: source_range, + source_range, })) } diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index 9838c162..dfac2d3f 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -160,10 +160,10 @@ impl HasDocumentSymbol for ArchitectureBody { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(String::from(format!( + detail: Some(format!( "architecture of {}", self.entity_name.item.item.name().to_string() - ))), + )), kind: symbol_kind(EntityClass::Architecture), deprecated: None, range: to_lsp_range(self.source_range.range()), From 2a6f388a8d85aceca53b86fb5f08074016f4d213 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 15:44:38 +0200 Subject: [PATCH 06/14] cargo fmt --- vhdl_ls/src/document_symbol.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index dfac2d3f..0c310f96 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -236,7 +236,10 @@ mod tests { 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) + ( + Url::from_file_path(file.path().canonicalize().unwrap()).unwrap(), + file, + ) } #[test] @@ -503,17 +506,15 @@ end; let response = nested_document_symbol_response_from_file(&source_url).unwrap(); assert_eq!( response, - DocumentSymbolResponse::from(vec![ - DocumentSymbol { - name: String::from("ent1"), - detail: Some(String::from("entity")), - kind: SymbolKind::Interface, - deprecated: None, - range: range((1, 0), (6, 4)), - selection_range: range((5, 7), (5, 11)), - children: None, - } - ]) + DocumentSymbolResponse::from(vec![DocumentSymbol { + name: String::from("ent1"), + detail: Some(String::from("entity")), + kind: SymbolKind::Interface, + deprecated: None, + range: range((1, 0), (6, 4)), + selection_range: range((5, 7), (5, 11)), + children: None, + }]) ); } } From 0f1f5139e20de43bda792c1a807adf8b2dc77030 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 20:37:26 +0200 Subject: [PATCH 07/14] Add HasSymbolKind trait --- vhdl_ls/src/document_symbol.rs | 96 ++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index 0c310f96..7faaf538 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -6,16 +6,27 @@ use lsp_types::{DocumentSymbol, DocumentSymbolResponse, SymbolKind, Url}; use vhdl_lang::ast::*; -use vhdl_lang::VHDLParser; +use vhdl_lang::{Source, VHDLParser}; pub fn nested_document_symbol_response_from_file(uri: &Url) -> Option { - if let Some(design_file) = parse_file(uri) { - Some(nested_document_symbol_response(&design_file)) - } else { - None + 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() { @@ -24,7 +35,7 @@ pub fn nested_document_symbol_response(design_file: &DesignFile) -> DocumentSymb DocumentSymbolResponse::from(response) } -fn symbol_kind(entity_class: EntityClass) -> SymbolKind { +fn symbol_kind_from_entity_class(entity_class: EntityClass) -> SymbolKind { match entity_class { EntityClass::Entity => SymbolKind::Interface, EntityClass::Architecture => SymbolKind::Class, @@ -34,8 +45,8 @@ fn symbol_kind(entity_class: EntityClass) -> SymbolKind { } } -fn symbol_kind_for_context() -> SymbolKind { - SymbolKind::Namespace +pub trait HasSymbolKind { + fn symbol_kind(&self) -> SymbolKind; } trait HasDocumentSymbol { @@ -63,12 +74,18 @@ impl HasDocumentSymbol for AnyPrimaryUnit { } } +impl HasSymbolKind for EntityDeclaration { + fn symbol_kind(&self) -> SymbolKind { + symbol_kind_from_entity_class(EntityClass::Entity) + } +} + impl HasDocumentSymbol for EntityDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), detail: Some(String::from("entity")), - kind: symbol_kind(EntityClass::Entity), + kind: self.symbol_kind(), deprecated: None, range: to_lsp_range(self.source_range.range()), selection_range: to_lsp_range(self.ident.pos.range()), @@ -77,12 +94,18 @@ impl HasDocumentSymbol for EntityDeclaration { } } +impl HasSymbolKind for ConfigurationDeclaration { + fn symbol_kind(&self) -> SymbolKind { + symbol_kind_from_entity_class(EntityClass::Configuration) + } +} + impl HasDocumentSymbol for ConfigurationDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), detail: Some(String::from("configuration")), - kind: symbol_kind(EntityClass::Configuration), + kind: self.symbol_kind(), deprecated: None, range: to_lsp_range(self.source_range.range()), selection_range: to_lsp_range(self.ident.pos.range()), @@ -91,12 +114,18 @@ impl HasDocumentSymbol for ConfigurationDeclaration { } } +impl HasSymbolKind for PackageDeclaration { + fn symbol_kind(&self) -> SymbolKind { + symbol_kind_from_entity_class(EntityClass::Package) + } +} + impl HasDocumentSymbol for PackageDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), detail: Some(String::from("package")), - kind: symbol_kind(EntityClass::Package), + kind: self.symbol_kind(), deprecated: None, range: to_lsp_range(self.source_range.range()), selection_range: to_lsp_range(self.ident.pos.range()), @@ -105,12 +134,18 @@ impl HasDocumentSymbol for PackageDeclaration { } } +impl HasSymbolKind for PackageInstantiation { + fn symbol_kind(&self) -> SymbolKind { + symbol_kind_from_entity_class(EntityClass::Package) + } +} + impl HasDocumentSymbol for PackageInstantiation { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), detail: Some(String::from("package instance")), - kind: symbol_kind(EntityClass::Package), + kind: self.symbol_kind(), deprecated: None, range: to_lsp_range(self.source_range.range()), selection_range: to_lsp_range(self.ident.pos.range()), @@ -119,12 +154,18 @@ impl HasDocumentSymbol for PackageInstantiation { } } +impl HasSymbolKind for ContextDeclaration { + fn symbol_kind(&self) -> SymbolKind { + SymbolKind::Namespace + } +} + impl HasDocumentSymbol for ContextDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), detail: Some(String::from("context")), - kind: symbol_kind_for_context(), + kind: self.symbol_kind(), deprecated: None, range: to_lsp_range(self.source_range.range()), selection_range: to_lsp_range(self.ident.pos.range()), @@ -142,12 +183,18 @@ impl HasDocumentSymbol for AnySecondaryUnit { } } +impl HasSymbolKind for PackageBody { + fn symbol_kind(&self) -> SymbolKind { + symbol_kind_from_entity_class(EntityClass::Package) + } +} + impl HasDocumentSymbol for PackageBody { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.item.name_utf8(), detail: Some(String::from("package body")), - kind: symbol_kind(EntityClass::Package), + kind: self.symbol_kind(), deprecated: None, range: to_lsp_range(self.source_range.range()), selection_range: to_lsp_range(self.ident.item.pos.range()), @@ -156,6 +203,12 @@ impl HasDocumentSymbol for PackageBody { } } +impl HasSymbolKind for ArchitectureBody { + fn symbol_kind(&self) -> SymbolKind { + symbol_kind_from_entity_class(EntityClass::Architecture) + } +} + impl HasDocumentSymbol for ArchitectureBody { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { @@ -164,7 +217,7 @@ impl HasDocumentSymbol for ArchitectureBody { "architecture of {}", self.entity_name.item.item.name().to_string() )), - kind: symbol_kind(EntityClass::Architecture), + kind: self.symbol_kind(), deprecated: None, range: to_lsp_range(self.source_range.range()), selection_range: to_lsp_range(self.ident.pos.range()), @@ -173,19 +226,6 @@ impl HasDocumentSymbol for ArchitectureBody { } } -fn parse_file(uri: &Url) -> Option { - match uri.to_file_path() { - Ok(url) => { - let mut diagnostics = vec![]; - match VHDLParser::default().parse_design_file(&url, &mut diagnostics) { - Ok((_, design_file)) => Some(design_file), - Err(_) => None, - } - } - _ => None, - } -} - fn to_lsp_pos(position: vhdl_lang::Position) -> lsp_types::Position { lsp_types::Position { line: position.line as u64, From 404f84157093b3d0e07709124ddc995069840afd Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 20:42:49 +0200 Subject: [PATCH 08/14] Add textDocument/documentSymbol for design units --- vhdl_ls/src/stdio_server.rs | 11 ++++++++++- vhdl_ls/src/vhdl_server.rs | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/vhdl_ls/src/stdio_server.rs b/vhdl_ls/src/stdio_server.rs index b561f672..30a7d832 100644 --- a/vhdl_ls/src/stdio_server.rs +++ b/vhdl_ls/src/stdio_server.rs @@ -85,7 +85,7 @@ pub fn start() { serde_json::to_value(value).map_err(|_| jsonrpc_core::Error::internal_error()) }); - let server = lang_server; + let server = lang_server.clone(); io.add_method("textDocument/references", move |params: Params| { let value = server .lock() @@ -94,6 +94,15 @@ pub fn start() { serde_json::to_value(value).map_err(|_| jsonrpc_core::Error::internal_error()) }); + let server = lang_server; + io.add_method("textDocument/documentSymbol", move |params: Params| { + let value = server + .lock() + .unwrap() + .text_document_document_symbol(¶ms.parse().unwrap()); + serde_json::to_value(value).map_err(|_| jsonrpc_core::Error::internal_error()) + }); + // Spawn thread to read requests from stdin spawn(move || { let stdin = io::stdin(); diff --git a/vhdl_ls/src/vhdl_server.rs b/vhdl_ls/src/vhdl_server.rs index 8b3d20ed..c93e15a3 100644 --- a/vhdl_ls/src/vhdl_server.rs +++ b/vhdl_ls/src/vhdl_server.rs @@ -11,6 +11,9 @@ use fnv; use std::collections::hash_map::Entry; use self::vhdl_lang::{Config, Diagnostic, Message, Project, Severity, Source, SrcPos}; +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}; @@ -150,6 +153,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 { @@ -192,6 +203,7 @@ impl InitializedVHDLServer { capabilities.declaration_provider = Some(true); capabilities.definition_provider = Some(true); capabilities.references_provider = Some(true); + capabilities.document_symbol_provider = Some(true); let result = InitializeResult { capabilities, server_info: None, @@ -342,6 +354,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 c40ec06616dc013f9533fdc5848b1133359900ef Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 21:40:01 +0200 Subject: [PATCH 09/14] Copyright year --- vhdl_lang/src/ast.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/stdio_server.rs | 2 +- vhdl_ls/src/vhdl_server.rs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index cd13ec8a..f54e32ac 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) 2020, 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/syntax/configuration.rs b/vhdl_lang/src/syntax/configuration.rs index 9df38f6f..3bbeee94 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) 2020, 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 2f429756..3274cea5 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) 2020, 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 20d358bc..46be63a4 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) 2020, 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 9caf1a79..a6e39099 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) 2020, Olof Kraigher olof.kraigher@gmail.com use super::tokens::{Kind::*, TokenStream}; diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index 5a3ca367..0558179d 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) 2020, Olof Kraigher olof.kraigher@gmail.com use super::common::ParseResult; use super::concurrent_statement::parse_labeled_concurrent_statement; diff --git a/vhdl_ls/src/stdio_server.rs b/vhdl_ls/src/stdio_server.rs index 30a7d832..6049d920 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) 2020, Olof Kraigher olof.kraigher@gmail.com use jsonrpc_core; diff --git a/vhdl_ls/src/vhdl_server.rs b/vhdl_ls/src/vhdl_server.rs index c93e15a3..3c3662c2 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) 2020, Olof Kraigher olof.kraigher@gmail.com use lsp_types::*; From 5fbccdabd2e332a6a37e653910d60b6c1967f4f0 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 22:35:44 +0200 Subject: [PATCH 10/14] Fix clippy 1.43 --- vhdl_lang/src/config.rs | 9 +++------ vhdl_lang/src/data/source.rs | 5 ++--- vhdl_lang/src/data/symbol_table.rs | 5 ++--- vhdl_ls/src/main.rs | 5 +---- vhdl_ls/src/rpc_channel.rs | 3 +-- vhdl_ls/src/stdio_server.rs | 9 ++------- vhdl_ls/src/vhdl_server.rs | 6 ++---- 7 files changed, 13 insertions(+), 29 deletions(-) diff --git a/vhdl_lang/src/config.rs b/vhdl_lang/src/config.rs index edf011f7..eeea1da7 100644 --- a/vhdl_lang/src/config.rs +++ b/vhdl_lang/src/config.rs @@ -2,21 +2,18 @@ // 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) 2020, Olof Kraigher olof.kraigher@gmail.com //! Configuration of the design hierarchy and other settings -use toml; - -use self::fnv::FnvHashMap; -use self::toml::Value; use crate::data::*; -use fnv; +use fnv::FnvHashMap; use std::env; use std::fs::File; use std::io; use std::io::prelude::*; use std::path::Path; +use toml::Value; #[derive(Clone, PartialEq, Default, Debug)] pub struct Config { diff --git a/vhdl_lang/src/data/source.rs b/vhdl_lang/src/data/source.rs index 51f19454..e048a7ef 100644 --- a/vhdl_lang/src/data/source.rs +++ b/vhdl_lang/src/data/source.rs @@ -2,11 +2,10 @@ // 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) 2020, Olof Kraigher olof.kraigher@gmail.com use super::contents::Contents; use super::diagnostic::{Diagnostic, DiagnosticResult}; -use pad; use parking_lot::{RwLock, RwLockReadGuard}; use std::cmp::{max, min}; use std::collections::hash_map::DefaultHasher; @@ -399,7 +398,7 @@ impl SrcPos { context_lines: u32, ) -> (usize, String) { let lines = self.get_line_context(context_lines, contents); - use self::pad::{Alignment, PadStr}; + use pad::{Alignment, PadStr}; // +1 since lines are shown with 1-index let lineno_len = (self.range.start.line + context_lines + 1) .to_string() diff --git a/vhdl_lang/src/data/symbol_table.rs b/vhdl_lang/src/data/symbol_table.rs index 1d51a345..85aaae79 100644 --- a/vhdl_lang/src/data/symbol_table.rs +++ b/vhdl_lang/src/data/symbol_table.rs @@ -2,14 +2,13 @@ // 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) 2020, Olof Kraigher olof.kraigher@gmail.com use super::latin_1::Latin1String; use parking_lot::RwLock; use std::sync::Arc; -use self::fnv::FnvHashMap; -use fnv; +use fnv::FnvHashMap; /// Represents a unique string symbol. /// diff --git a/vhdl_ls/src/main.rs b/vhdl_ls/src/main.rs index 3721aa82..b0b5e218 100644 --- a/vhdl_ls/src/main.rs +++ b/vhdl_ls/src/main.rs @@ -2,13 +2,10 @@ // 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 - -use vhdl_ls; +// Copyright (c) 2020, Olof Kraigher olof.kraigher@gmail.com #[macro_use] extern crate log; -use env_logger; fn main() { use clap::App; diff --git a/vhdl_ls/src/rpc_channel.rs b/vhdl_ls/src/rpc_channel.rs index ca3b4df8..647545a8 100644 --- a/vhdl_ls/src/rpc_channel.rs +++ b/vhdl_ls/src/rpc_channel.rs @@ -2,12 +2,11 @@ // 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) 2020, Olof Kraigher olof.kraigher@gmail.com //! Contains the RpcChannel Traid and associated convenience functions use lsp_types::*; -use serde; use vhdl_lang::{Message, MessageHandler}; pub trait RpcChannel { diff --git a/vhdl_ls/src/stdio_server.rs b/vhdl_ls/src/stdio_server.rs index 6049d920..43d094c6 100644 --- a/vhdl_ls/src/stdio_server.rs +++ b/vhdl_ls/src/stdio_server.rs @@ -4,13 +4,8 @@ // // Copyright (c) 2020, Olof Kraigher olof.kraigher@gmail.com -use jsonrpc_core; - -use serde; -use serde_json; - -use self::jsonrpc_core::request::Notification; -use self::jsonrpc_core::{IoHandler, Params}; +use jsonrpc_core::request::Notification; +use jsonrpc_core::{IoHandler, Params}; use std::io::prelude::*; use std::io::{self, BufRead}; diff --git a/vhdl_ls/src/vhdl_server.rs b/vhdl_ls/src/vhdl_server.rs index 3c3662c2..819540fc 100644 --- a/vhdl_ls/src/vhdl_server.rs +++ b/vhdl_ls/src/vhdl_server.rs @@ -6,18 +6,16 @@ use lsp_types::*; -use self::fnv::FnvHashMap; -use fnv; +use fnv::FnvHashMap; use std::collections::hash_map::Entry; -use self::vhdl_lang::{Config, Diagnostic, Message, Project, Severity, Source, SrcPos}; 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}; -use vhdl_lang; +use vhdl_lang::{Config, Diagnostic, Message, Project, Severity, Source, SrcPos}; pub struct VHDLServer { rpc_channel: T, From 4656ac217765beb4bddae843d13b32f2e8d1aa75 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 26 Apr 2020 22:55:41 +0200 Subject: [PATCH 11/14] Remove redundant match from document symbol tests --- vhdl_ls/src/document_symbol.rs | 69 ++++------------------------------ 1 file changed, 7 insertions(+), 62 deletions(-) diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index 7faaf538..2adf21b8 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -331,17 +331,8 @@ configuration cfg of entity_name is end; ", ); - assert_eq!(design_file.design_units.len(), 1); - let configuration = match design_file.design_units.first().unwrap() { - AnyDesignUnit::Primary(primary) => match primary { - AnyPrimaryUnit::Configuration(configuration) => configuration, - _ => panic!("expected configuration declaration"), - }, - _ => panic!("expected configuration declaration"), - }; - assert_eq!( - configuration.document_symbol(), + design_file.design_units.first().unwrap().document_symbol(), DocumentSymbol { name: String::from("cfg"), detail: Some(String::from("configuration")), @@ -366,17 +357,8 @@ package pkg is end; ", ); - assert_eq!(design_file.design_units.len(), 1); - let package = match design_file.design_units.first().unwrap() { - AnyDesignUnit::Primary(primary) => match primary { - AnyPrimaryUnit::Package(package) => package, - _ => panic!("expected package declaration"), - }, - _ => panic!("expected package declaration"), - }; - assert_eq!( - package.document_symbol(), + design_file.design_units.first().unwrap().document_symbol(), DocumentSymbol { name: String::from("pkg"), detail: Some(String::from("package")), @@ -403,17 +385,8 @@ package pkg_inst is new work.pkg ); ", ); - assert_eq!(design_file.design_units.len(), 1); - let package_instance = match design_file.design_units.first().unwrap() { - AnyDesignUnit::Primary(primary) => match primary { - AnyPrimaryUnit::PackageInstance(package_instance) => package_instance, - _ => panic!("expected package declaration"), - }, - _ => panic!("expected package declaration"), - }; - assert_eq!( - package_instance.document_symbol(), + design_file.design_units.first().unwrap().document_symbol(), DocumentSymbol { name: String::from("pkg_inst"), detail: Some(String::from("package instance")), @@ -437,17 +410,8 @@ context ctx is end; ", ); - assert_eq!(design_file.design_units.len(), 1); - let context = match design_file.design_units.first().unwrap() { - AnyDesignUnit::Primary(primary) => match primary { - AnyPrimaryUnit::Context(context) => context, - _ => panic!("expected package declaration"), - }, - _ => panic!("expected package declaration"), - }; - assert_eq!( - context.document_symbol(), + design_file.design_units.first().unwrap().document_symbol(), DocumentSymbol { name: String::from("ctx"), detail: Some(String::from("context")), @@ -472,17 +436,8 @@ package body pkg is end; ", ); - assert_eq!(design_file.design_units.len(), 1); - let package_body = match design_file.design_units.first().unwrap() { - AnyDesignUnit::Secondary(secondary) => match secondary { - AnySecondaryUnit::PackageBody(package_body) => package_body, - _ => panic!("expected package declaration"), - }, - _ => panic!("expected package declaration"), - }; - assert_eq!( - package_body.document_symbol(), + design_file.design_units.first().unwrap().document_symbol(), DocumentSymbol { name: String::from("pkg"), detail: Some(String::from("package body")), @@ -508,17 +463,8 @@ begin end; ", ); - assert_eq!(design_file.design_units.len(), 1); - let architecture = match design_file.design_units.first().unwrap() { - AnyDesignUnit::Secondary(secondary) => match secondary { - AnySecondaryUnit::Architecture(architecture) => architecture, - _ => panic!("expected package declaration"), - }, - _ => panic!("expected package declaration"), - }; - assert_eq!( - architecture.document_symbol(), + design_file.design_units.first().unwrap().document_symbol(), DocumentSymbol { name: String::from("rtl"), detail: Some(String::from("architecture of ent")), @@ -543,9 +489,8 @@ entity ent1 is end; ", ); - let response = nested_document_symbol_response_from_file(&source_url).unwrap(); assert_eq!( - response, + nested_document_symbol_response_from_file(&source_url).unwrap(), DocumentSymbolResponse::from(vec![DocumentSymbol { name: String::from("ent1"), detail: Some(String::from("entity")), From 2ad24cdce8f9642a58877df23400a56ad9d41586 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sat, 2 May 2020 12:48:08 +0200 Subject: [PATCH 12/14] Remove hard coded ranges from tests --- vhdl_lang/src/syntax/configuration.rs | 20 +++++----- vhdl_lang/src/syntax/context.rs | 15 ++------ vhdl_lang/src/syntax/declarative_part.rs | 4 +- vhdl_lang/src/syntax/design_unit.rs | 48 ++++++++++++------------ vhdl_lang/src/syntax/test.rs | 27 ++++++++----- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/vhdl_lang/src/syntax/configuration.rs b/vhdl_lang/src/syntax/configuration.rs index 3bbeee94..bb4dd9dc 100644 --- a/vhdl_lang/src/syntax/configuration.rs +++ b/vhdl_lang/src/syntax/configuration.rs @@ -378,7 +378,7 @@ end; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, (0, 0), (3, 4)) + source_range: source_range(&code, "configuration", "end;") } ); } @@ -406,7 +406,7 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, (0, 0), (3, 22)), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -438,7 +438,7 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, (0, 0), (5, 22)), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -472,7 +472,7 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, (0, 0), (5, 22)), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -500,7 +500,7 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, (0, 0), (3, 22)), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -543,7 +543,7 @@ end configuration cfg; }) ], }, - source_range: source_range(&code, (0, 0), (7, 22)), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -589,7 +589,7 @@ end configuration cfg; }), }),], }, - source_range: source_range(&code, (0, 0), (7, 22)), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -646,7 +646,7 @@ end configuration cfg; }), }),], }, - source_range: source_range(&code, (0, 0), (9, 22)), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -694,7 +694,7 @@ end configuration cfg; block_config: None, }),], }, - source_range: source_range(&code, (0, 0), (6, 22)), + source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), } ); } @@ -773,7 +773,7 @@ end configuration cfg; }) ], }, - source_range: source_range(&code, (0, 0), (11, 22)), + 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 3274cea5..29537043 100644 --- a/vhdl_lang/src/syntax/context.rs +++ b/vhdl_lang/src/syntax/context.rs @@ -247,21 +247,14 @@ context ident is end context ident; ", ]; - let source_ranges = vec![ - ((0, 0), (1, 4)), - ((0, 0), (1, 12)), - ((0, 0), (1, 10)), - ((0, 0), (1, 18)), - ]; - for variant in variants.iter().zip(source_ranges.iter()) { - let (variant, (start, end)) = variant; + for variant in variants.iter() { let code = Code::new(variant); assert_eq!( code.with_stream_no_diagnostics(parse_context), DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), items: vec![], - source_range: source_range(&code, *start, *end), + source_range: source_range(&code, "context", ";"), }) ); } @@ -288,7 +281,7 @@ end context ident2; DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), items: vec![], - source_range: source_range(&code, (0, 0), (1, 19)), + source_range: source_range(&code, "context ident", "end context ident2;"), }) ); } @@ -328,7 +321,7 @@ end context; code.s1("context foo.ctx;") ), ], - source_range: source_range(&code, (0, 0), (4, 12)), + 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 46be63a4..731497c1 100644 --- a/vhdl_lang/src/syntax/declarative_part.rs +++ b/vhdl_lang/src/syntax/declarative_part.rs @@ -182,7 +182,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, - source_range: source_range(&code, (0, 0), (0, 33)), + source_range: source_range(&code, "package", ";"), } ); } @@ -209,7 +209,7 @@ package ident is new lib.foo.bar )") .association_list() ), - source_range: source_range(&code, (0, 0), (3, 4)), + 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 a6e39099..630b06fc 100644 --- a/vhdl_lang/src/syntax/design_unit.rs +++ b/vhdl_lang/src/syntax/design_unit.rs @@ -356,7 +356,7 @@ end entity; design_file.design_units, [simple_entity( code.s1("myent").ident(), - source_range(&code, (1, 0), (2, 11)) + source_range(&code, "entity", ";") )] ); @@ -370,7 +370,7 @@ end entity myent; design_file.design_units, [simple_entity( code.s1("myent").ident(), - source_range(&code, (1, 0), (2, 17)) + source_range(&code, "entity", ";") )] ); } @@ -393,7 +393,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![], - source_range: source_range(&code, (1, 0), (3, 11)), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -421,7 +421,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![], - source_range: source_range(&code, (1, 0), (4, 11)), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -444,7 +444,7 @@ end entity; port_clause: Some(vec![]), decl: vec![], statements: vec![], - source_range: source_range(&code, (1, 0), (3, 11)), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -467,7 +467,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![], - source_range: source_range(&code, (1, 0), (3, 11)), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -490,7 +490,7 @@ end entity; port_clause: None, decl: code.s1("constant foo : natural := 0;").declarative_part(), statements: vec![], - source_range: source_range(&code, (1, 0), (3, 11)), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -514,7 +514,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![code.s1("check(clk, valid);").concurrent_statement()], - source_range: source_range(&code, (1, 0), (4, 11)), + source_range: source_range(&code, "entity", "end entity;"), } ); } @@ -541,19 +541,19 @@ end; [ simple_entity( code.s1("myent").ident(), - source_range(&code, (1, 0), (2, 11)) + source_range(&code, "entity myent", "end entity;") ), simple_entity( code.s1("myent2").ident(), - source_range(&code, (4, 0), (5, 18)) + source_range(&code, "entity myent2", "end entity myent2;") ), simple_entity( code.s1("myent3").ident(), - source_range(&code, (7, 0), (8, 11)) + source_range(&code, "entity myent3", "end myent3;") ), simple_entity( code.s1("myent4").ident(), - source_range(&code, (10, 0), (11, 4)) + source_range(&code, "entity myent4", "end;") ) ] ); @@ -589,7 +589,7 @@ end architecture; [simple_architecture( code.s1("arch_name").ident(), code.s1("myent").ident(), - source_range(&code, (1, 0), (3, 17)) + source_range(&code, "architecture", ";") )] ); } @@ -608,7 +608,7 @@ end architecture arch_name; [simple_architecture( code.s1("arch_name").ident(), code.s1("myent").ident(), - source_range(&code, (1, 0), (3, 27)) + source_range(&code, "architecture", ";") )] ); } @@ -627,7 +627,7 @@ end; [simple_architecture( code.s1("arch_name").ident(), code.s1("myent").ident(), - source_range(&code, (1, 0), (3, 4)) + source_range(&code, "architecture", ";") )] ); } @@ -647,7 +647,7 @@ end package; ident: code.s1("pkg_name").ident(), generic_clause: None, decl: vec![], - source_range: source_range(&code, (1, 0), (2, 12)), + source_range: source_range(&code, "package", ";"), } ); } @@ -674,7 +674,7 @@ end package; constant bar : natural := 0; ") .declarative_part(), - source_range: source_range(&code, (1, 0), (4, 12)), + source_range: source_range(&code, "package", "end package;"), } ); } @@ -701,7 +701,7 @@ end package; code.s1("type bar").generic() ]), decl: vec![], - source_range: source_range(&code, (1, 0), (6, 12)), + source_range: source_range(&code, "package", "end package;"), } ); } @@ -735,7 +735,7 @@ end entity; port_clause: None, decl: vec![], statements: vec![], - source_range: source_range(&code, (1, 0), (5, 11)), + source_range: source_range(&code, "library", "end entity;"), } ))] } @@ -772,7 +772,7 @@ end; entity_name: code.s1("myent").ident().into_ref(), decl: Vec::new(), statements: vec![], - source_range: source_range(&code, (1, 0), (6, 4)), + source_range: source_range(&code, "library", "end;"), } ))], } @@ -807,7 +807,7 @@ end; ident: code.s1("pkg_name").ident(), generic_clause: None, decl: vec![], - source_range: source_range(&code, (1, 0), (5, 4)), + source_range: source_range(&code, "library", "end;"), } ))], } @@ -841,7 +841,7 @@ end; ], ident: code.s1("pkg_name").ident().into_ref(), decl: vec![], - source_range: source_range(&code, (1, 0), (5, 4)), + source_range: source_range(&code, "library", "end;"), } ))], } @@ -883,7 +883,7 @@ end; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, (1, 0), (7, 4)) + source_range: source_range(&code, "library", "end;") } ))], } @@ -916,7 +916,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, - source_range: source_range(&code, (1, 0), (4, 33)), + source_range: source_range(&code, "library", "bar;"), } ))], } diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index 0558179d..8945828f 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -569,16 +569,23 @@ impl AsRef for Code { } } -pub fn source_range(code: &Code, start: (u32, u32), end: (u32, u32)) -> SrcPos { - let (start_line, start_column) = start; - let (end_line, end_column) = end; - SrcPos::new( - code.source().clone(), - crate::data::Range::new( - Position::new(start_line, start_column), - Position::new(end_line, end_column), - ), - ) +// Create a Range spanning from the first substring occurance of start to +// to the first occurance of end after start (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"); + } + } } mod tests { From 718545d20835d92900c682ddd2d610430d382881 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sat, 2 May 2020 12:49:10 +0200 Subject: [PATCH 13/14] cargo fmt --- vhdl_lang/src/syntax/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index 8945828f..098cede5 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -579,7 +579,7 @@ pub fn source_range(code: &Code, start: &str, end: &str) -> SrcPos { if end.range().start.line >= start.range().start.line && end.range().end.line >= start.range().end.line { - break start.combine_into(&end) + break start.combine_into(&end); } end_occurance += 1; if end_occurance > 1000 { From 42bb5062c7b031c0eea900b5efd45641c8fab148 Mon Sep 17 00:00:00 2001 From: Henrik Bohlin Date: Sun, 17 May 2020 11:12:27 +0200 Subject: [PATCH 14/14] Add token fields to AST and add document symbols --- vhdl_lang/src/analysis/concurrent.rs | 2 + vhdl_lang/src/analysis/design_unit.rs | 6 +- vhdl_lang/src/ast.rs | 79 +- vhdl_lang/src/ast/search.rs | 9 + vhdl_lang/src/data.rs | 1 + vhdl_lang/src/syntax.rs | 2 +- vhdl_lang/src/syntax/component_declaration.rs | 55 +- vhdl_lang/src/syntax/concurrent_statement.rs | 50 +- vhdl_lang/src/syntax/configuration.rs | 65 +- vhdl_lang/src/syntax/context.rs | 30 +- vhdl_lang/src/syntax/declarative_part.rs | 30 +- vhdl_lang/src/syntax/design_unit.rs | 296 ++-- vhdl_lang/src/syntax/test.rs | 84 +- vhdl_lang/src/syntax/tokens/tokenizer.rs | 17 + vhdl_ls/src/document_symbol.rs | 1355 ++++++++++++++--- 15 files changed, 1677 insertions(+), 404 deletions(-) diff --git a/vhdl_lang/src/analysis/concurrent.rs b/vhdl_lang/src/analysis/concurrent.rs index 8a6f3b25..201f8e23 100644 --- a/vhdl_lang/src/analysis/concurrent.rs +++ b/vhdl_lang/src/analysis/concurrent.rs @@ -64,6 +64,8 @@ impl<'a> AnalyzeContext<'a> { sensitivity_list, decl, statements, + start_token: _, + semi_token: _, } = 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 1b666f9e..40037544 100644 --- a/vhdl_lang/src/analysis/design_unit.rs +++ b/vhdl_lang/src/analysis/design_unit.rs @@ -75,10 +75,10 @@ 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.items, 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.items, diagnostics)?; } self.analyze_declarative_part(&mut primary_region, &mut unit.decl, diagnostics)?; self.analyze_concurrent_part(&mut primary_region, &mut unit.statements, diagnostics)?; @@ -148,7 +148,7 @@ 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.items, diagnostics)?; } self.analyze_declarative_part(&mut primary_region, &mut unit.decl, diagnostics)?; diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index f54e32ac..7654a165 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -24,6 +24,8 @@ pub use any_design_unit::*; use crate::data::*; +pub use crate::data::KeyWordToken; + /// LRM 15.8 Bit string literals #[derive(PartialEq, Copy, Clone, Debug)] pub enum BaseSpecifier { @@ -622,6 +624,15 @@ pub enum InterfaceDeclaration { Package(InterfacePackageDeclaration), } +#[derive(PartialEq, Debug, Clone)] +pub struct InterfaceList { + pub items: Vec, + + // Tokens + pub start_token: KeyWordToken, // port/generiv/parameter + pub semi_token: KeyWordToken, +} + #[derive(PartialEq, Debug, Clone, Copy)] pub enum Mode { In, @@ -631,11 +642,6 @@ pub enum Mode { Linkage, } -#[derive(PartialEq, Debug, Clone)] -pub struct PortClause { - pub port_list: Vec, -} - /// LRM 6.8 Component declarations #[derive(PartialEq, Debug, Clone)] pub struct ComponentDeclaration { @@ -859,6 +865,10 @@ pub struct BlockStatement { pub header: BlockHeader, pub decl: Vec, pub statements: Vec, + + // Tokens + pub block_token: KeyWordToken, + pub semi_token: KeyWordToken, } /// LRM 11.2 Block statement @@ -883,6 +893,10 @@ pub struct ProcessStatement { pub sensitivity_list: Option, pub decl: Vec, pub statements: Vec, + + // Tokens + pub start_token: KeyWordToken, // postponed/process + pub semi_token: KeyWordToken, } /// LRM 11.4 Concurrent procedure call statements @@ -998,8 +1012,11 @@ pub struct ContextDeclaration { pub ident: Ident, pub items: ContextClause, - // Non-LRM fields - pub source_range: SrcPos, + // Tokens + pub context_token: KeyWordToken, + pub is_token: KeyWordToken, + pub end_token: KeyWordToken, + pub semi_token: KeyWordToken, } /// LRM 4.9 Package instatiation declaration @@ -1010,8 +1027,9 @@ pub struct PackageInstantiation { pub package_name: WithPos, pub generic_map: Option>, - // Non-LRM fields - pub source_range: SrcPos, + // Tokens + pub package_token: KeyWordToken, + pub semi_token: KeyWordToken, } /// LRM 7.3 Configuration specification @@ -1100,8 +1118,11 @@ pub struct ConfigurationDeclaration { pub vunit_bind_inds: Vec, pub block_config: BlockConfiguration, - // Non-LRM fields - pub source_range: SrcPos, + // Tokens + pub configuration_token: KeyWordToken, + pub is_token: KeyWordToken, + pub end_token: KeyWordToken, + pub semi_token: KeyWordToken, } /// LRM 3.2 Entity declarations @@ -1109,13 +1130,17 @@ pub struct ConfigurationDeclaration { pub struct EntityDeclaration { pub context_clause: ContextClause, pub ident: Ident, - pub generic_clause: Option>, - pub port_clause: Option>, + pub generic_clause: Option, + pub port_clause: Option, pub decl: Vec, pub statements: Vec, - // Non-LRM fields - pub source_range: SrcPos, + // Tokens + pub entity_token: KeyWordToken, + pub is_token: KeyWordToken, + pub begin_token: Option, + pub end_token: KeyWordToken, + pub semi_token: KeyWordToken, } /// LRM 3.3 Architecture bodies #[derive(PartialEq, Debug, Clone)] @@ -1126,8 +1151,12 @@ pub struct ArchitectureBody { pub decl: Vec, pub statements: Vec, - // Non-LRM fields - pub source_range: SrcPos, + // Tokens + pub architecture_token: KeyWordToken, + pub is_token: KeyWordToken, + pub begin_token: KeyWordToken, + pub end_token: KeyWordToken, + pub semi_token: KeyWordToken, } /// LRM 4.7 Package declarations @@ -1135,11 +1164,14 @@ pub struct ArchitectureBody { pub struct PackageDeclaration { pub context_clause: ContextClause, pub ident: Ident, - pub generic_clause: Option>, + pub generic_clause: Option, pub decl: Vec, - // Non-LRM fields - pub source_range: SrcPos, + // Tokens + pub package_token: KeyWordToken, + pub is_token: KeyWordToken, + pub end_token: KeyWordToken, + pub semi_token: KeyWordToken, } /// LRM 4.8 Package bodies @@ -1149,8 +1181,11 @@ pub struct PackageBody { pub ident: WithRef, pub decl: Vec, - // Non-LRM fields - pub source_range: SrcPos, + // Tokens + pub package_token: KeyWordToken, + pub is_token: KeyWordToken, + pub end_token: KeyWordToken, + pub semi_token: KeyWordToken, } /// LRM 13.1 Design units diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index ff87d4f0..14e9f2df 100644 --- a/vhdl_lang/src/ast/search.rs +++ b/vhdl_lang/src/ast/search.rs @@ -389,6 +389,8 @@ impl Search for LabeledConcurrentStatement { sensitivity_list, decl, statements, + start_token: _, + semi_token: _, } = process; return_if_found!(sensitivity_list.search(searcher)); return_if_found!(decl.search(searcher)); @@ -883,6 +885,13 @@ impl Search for InterfaceDeclaration { } } +impl Search for InterfaceList { + fn search(&self, searcher: &mut impl Searcher) -> SearchResult { + return_if_found!(self.items.search(searcher)); + NotFound + } +} + impl Search for SubprogramDeclaration { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { match self { diff --git a/vhdl_lang/src/data.rs b/vhdl_lang/src/data.rs index 6ba7a451..07028445 100644 --- a/vhdl_lang/src/data.rs +++ b/vhdl_lang/src/data.rs @@ -12,6 +12,7 @@ mod message; mod source; mod symbol_table; +pub use crate::syntax::KeyWordToken; pub use contents::*; pub use diagnostic::*; pub use latin_1::*; diff --git a/vhdl_lang/src/syntax.rs b/vhdl_lang/src/syntax.rs index 173a4ca2..3dd43b8e 100644 --- a/vhdl_lang/src/syntax.rs +++ b/vhdl_lang/src/syntax.rs @@ -32,4 +32,4 @@ mod waveform; pub mod test; pub use parser::{ParserResult, VHDLParser}; -pub use tokens::Symbols; +pub use tokens::{KeyWordToken, Symbols}; diff --git a/vhdl_lang/src/syntax/component_declaration.rs b/vhdl_lang/src/syntax/component_declaration.rs index 3e3a9e2f..f3212631 100644 --- a/vhdl_lang/src/syntax/component_declaration.rs +++ b/vhdl_lang/src/syntax/component_declaration.rs @@ -8,13 +8,13 @@ use super::common::error_on_end_identifier_mismatch; 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::ast::{ComponentDeclaration, InterfaceList}; use crate::data::{Diagnostic, DiagnosticHandler}; 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,15 @@ 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(InterfaceList { + items: new_list, + start_token: token.into(), + semi_token: semi_token.into(), + }); } } _ => break, @@ -39,7 +43,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 +51,15 @@ 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(InterfaceList { + items: new_list, + start_token: token.into(), + semi_token: semi_token.into(), + }); } } Generic => { @@ -92,8 +100,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(), |list| list.items), + port_list: port_list.map_or(Vec::new(), |list| list.items), }) } @@ -101,7 +109,7 @@ pub fn parse_component_declaration( mod tests { use super::*; - use crate::ast::Ident; + use crate::ast::{Ident, InterfaceDeclaration}; use crate::syntax::test::Code; fn to_component( @@ -222,7 +230,14 @@ end "Duplicate generic clause" )] ); - assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").generic()])),); + assert_eq!( + result, + Ok(Some(InterfaceList { + items: vec![code.s1("foo : natural").generic()], + start_token: code.keyword_token(Generic, 1), + semi_token: code.keyword_token(SemiColon, 1) + })), + ); } #[test] @@ -246,7 +261,14 @@ end "Duplicate port clause" )] ); - assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").port()])),); + assert_eq!( + result, + Ok(Some(InterfaceList { + items: vec![code.s1("foo : natural").port()], + start_token: code.keyword_token(Port, 1), + semi_token: code.keyword_token(SemiColon, 1) + })), + ); } #[test] @@ -270,6 +292,13 @@ end "Generic clause must come before port clause" )] ); - assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").port()])),); + assert_eq!( + result, + Ok(Some(InterfaceList { + items: vec![code.s1("foo : natural").port()], + start_token: code.keyword_token(Port, 1), + semi_token: code.keyword_token(SemiColon, 1) + })), + ); } } diff --git a/vhdl_lang/src/syntax/concurrent_statement.rs b/vhdl_lang/src/syntax/concurrent_statement.rs index 6cea181b..82acba61 100644 --- a/vhdl_lang/src/syntax/concurrent_statement.rs +++ b/vhdl_lang/src/syntax/concurrent_statement.rs @@ -26,6 +26,7 @@ use crate::data::*; /// LRM 11.2 Block statement pub fn parse_block_statement( + block_token: Token, stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { @@ -48,12 +49,14 @@ 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)?.into(); Ok(BlockStatement { guard_condition, header, decl, statements, + block_token: block_token.into(), + semi_token, }) } @@ -153,9 +156,10 @@ fn parse_block_header( /// LRM 11.3 Process statement pub fn parse_process_statement( stream: &mut TokenStream, - postponed: bool, + start_token: Token, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { + let postponed = start_token.kind == Postponed; let token = stream.peek_expect()?; let sensitivity_list = { match token.kind { @@ -207,12 +211,14 @@ 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)?.into(); Ok(ProcessStatement { postponed, sensitivity_list, decl, statements, + start_token: start_token.into(), + semi_token, }) } @@ -557,10 +563,10 @@ pub fn parse_concurrent_statement( try_token_kind!( token, Block => { - ConcurrentStatement::Block(parse_block_statement(stream, diagnostics)?) + ConcurrentStatement::Block(parse_block_statement(token, stream, diagnostics)?) }, Process => { - ConcurrentStatement::Process(parse_process_statement(stream, false, diagnostics)?) + ConcurrentStatement::Process(parse_process_statement(stream, token, diagnostics)?) }, Component => { let unit = InstantiatedUnit::Component(parse_selected_name(stream)?); @@ -589,13 +595,13 @@ 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 token = stream.expect()?; - match token.kind { - Process => ConcurrentStatement::Process(parse_process_statement(stream, true, diagnostics)?), + let next_token = stream.expect()?; + match next_token.kind { + Process => ConcurrentStatement::Process(parse_process_statement(stream, token, diagnostics)?), Assert => ConcurrentStatement::Assert(parse_concurrent_assert_statement(stream, true)?), With => ConcurrentStatement::Assignment(parse_selected_signal_assignment(stream, true)?), _ => { - let target = parse_name_initial_token(stream, token)?.map_into(Target::Name); + let target = parse_name_initial_token(stream, next_token)?.map_into(Target::Name); stream.expect_kind(SemiColon)?; ConcurrentStatement::ProcedureCall(to_procedure_call(target, true)?) } @@ -799,6 +805,8 @@ end block; label: Some(code.s1("name2").ident()), statement: ConcurrentStatement::ProcedureCall(call), }], + block_token: code.keyword_token(Block, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -824,6 +832,8 @@ end block name; }, decl: vec![], statements: vec![], + block_token: code.keyword_token(Block, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -849,6 +859,8 @@ end block; }, decl: vec![], statements: vec![], + block_token: code.keyword_token(Block, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -874,6 +886,8 @@ end block; }, decl: vec![], statements: vec![], + block_token: code.keyword_token(Block, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -903,6 +917,8 @@ end block; }, decl: vec![], statements: vec![], + block_token: code.keyword_token(Block, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -923,6 +939,8 @@ end process; sensitivity_list: None, decl: vec![], statements: vec![], + start_token: code.keyword_token(Process, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -943,6 +961,8 @@ end process name; sensitivity_list: None, decl: vec![], statements: vec![], + start_token: code.keyword_token(Process, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, Some(code.s1("name").ident())); @@ -963,6 +983,8 @@ end process; sensitivity_list: None, decl: vec![], statements: vec![], + start_token: code.keyword_token(Postponed, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -983,6 +1005,8 @@ end postponed process; sensitivity_list: None, decl: vec![], statements: vec![], + start_token: code.keyword_token(Postponed, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -1004,6 +1028,8 @@ end postponed process; sensitivity_list: None, decl: Vec::new(), statements: Vec::new(), + start_token: code.keyword_token(Process, 1), + semi_token: code.keyword_token(SemiColon, -1), }; assert_eq!( diagnostics, @@ -1032,6 +1058,8 @@ end process; ])), decl: vec![], statements: vec![], + start_token: code.keyword_token(Process, 1), + semi_token: code.keyword_token(SemiColon, -1), }; let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement); assert_eq!(stmt.label, None); @@ -1053,6 +1081,8 @@ end process; sensitivity_list: Some(SensitivityList::Names(Vec::new())), decl: Vec::new(), statements: Vec::new(), + start_token: code.keyword_token(Process, 1), + semi_token: code.keyword_token(SemiColon, -1), }; assert_eq!( diagnostics, @@ -1084,6 +1114,8 @@ end process; code.s1("foo <= true;").sequential_statement(), code.s1("wait;").sequential_statement(), ], + start_token: code.keyword_token(Process, 1), + semi_token: code.keyword_token(SemiColon, -1), }; 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 bb4dd9dc..a7297fbd 100644 --- a/vhdl_lang/src/syntax/configuration.rs +++ b/vhdl_lang/src/syntax/configuration.rs @@ -268,11 +268,11 @@ pub fn parse_configuration_declaration( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - let configuration_token = stream.expect_kind(Configuration)?; + let configuration_token = stream.expect_kind(Configuration)?.into(); let ident = stream.expect_ident()?; stream.expect_kind(Of)?; let entity_name = parse_selected_name(stream)?; - stream.expect_kind(Is)?; + let is_token = stream.expect_kind(Is)?.into(); let mut decl = Vec::new(); let vunit_bind_inds = loop { @@ -295,13 +295,13 @@ pub fn parse_configuration_declaration( stream.expect_kind(For)?; let block_config = parse_block_configuration_known_keyword(stream, diagnostics)?; - stream.expect_kind(End)?; + let end_token = stream.expect_kind(End)?.into(); stream.pop_if_kind(Configuration)?; let end_ident = stream.pop_optional_ident()?; if let Some(diagnostic) = error_on_end_identifier_mismatch(&ident, &end_ident) { diagnostics.push(diagnostic) } - let semi_token = stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?.into(); Ok(ConfigurationDeclaration { context_clause: ContextClause::default(), ident, @@ -309,7 +309,10 @@ pub fn parse_configuration_declaration( decl, vunit_bind_inds, block_config, - source_range: configuration_token.pos.combine_into(&semi_token), + configuration_token, + is_token, + end_token, + semi_token, }) } @@ -352,7 +355,7 @@ pub fn parse_configuration_specification( #[cfg(test)] mod tests { use super::*; - use crate::syntax::test::{source_range, Code}; + use crate::syntax::test::Code; use pretty_assertions::assert_eq; #[test] @@ -378,7 +381,10 @@ end; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, "configuration", "end;") + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -406,7 +412,10 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -438,7 +447,10 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -472,7 +484,10 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -500,7 +515,10 @@ end configuration cfg; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -543,7 +561,10 @@ end configuration cfg; }) ], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -589,7 +610,10 @@ end configuration cfg; }), }),], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -646,7 +670,10 @@ end configuration cfg; }), }),], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -694,7 +721,10 @@ end configuration cfg; block_config: None, }),], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -773,7 +803,10 @@ end configuration cfg; }) ], }, - source_range: source_range(&code, "configuration cfg", "end configuration cfg;"), + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } diff --git a/vhdl_lang/src/syntax/context.rs b/vhdl_lang/src/syntax/context.rs index 29537043..39cd7c6e 100644 --- a/vhdl_lang/src/syntax/context.rs +++ b/vhdl_lang/src/syntax/context.rs @@ -89,10 +89,11 @@ pub fn parse_context( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - let context_token = stream.expect_kind(Context)?; + let context_token = stream.expect_kind(Context)?.into(); let name = parse_name(stream)?; - if stream.skip_if_kind(Is)? { + if let Some(is_token) = stream.pop_if_kind(Is)? { let mut items = Vec::with_capacity(16); + let end_token; let end_ident; loop { let token = stream.expect()?; @@ -102,13 +103,14 @@ pub fn parse_context( Use => items.push(parse_use_clause_no_keyword(token, stream)?.map_into(ContextItem::Use)), Context => items.push(parse_context_reference_no_keyword(token, stream)?.map_into(ContextItem::Context)), End => { + end_token = token.into(); stream.pop_if_kind(Context)?; end_ident = stream.pop_optional_ident()?; break; } ) } - let semi_token = stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?.into(); let ident = to_simple_name(name)?; diagnostics.push_some(error_on_end_identifier_mismatch(&ident, &end_ident)); @@ -116,7 +118,10 @@ pub fn parse_context( Ok(DeclarationOrReference::Declaration(ContextDeclaration { ident, items, - source_range: context_token.pos.combine_into(&semi_token), + context_token, + is_token: is_token.into(), + end_token, + semi_token, })) } else { // Context reference @@ -140,7 +145,7 @@ mod tests { use super::*; use crate::data::Diagnostic; - use crate::syntax::test::{source_range, Code}; + use crate::syntax::test::Code; use pretty_assertions::assert_eq; #[test] @@ -254,7 +259,10 @@ end context ident; DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), items: vec![], - source_range: source_range(&code, "context", ";"), + context_token: code.keyword_token(Context, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), }) ); } @@ -281,7 +289,10 @@ end context ident2; DeclarationOrReference::Declaration(ContextDeclaration { ident: code.s1("ident").ident(), items: vec![], - source_range: source_range(&code, "context ident", "end context ident2;"), + context_token: code.keyword_token(Context, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), }) ); } @@ -321,7 +332,10 @@ end context; code.s1("context foo.ctx;") ), ], - source_range: source_range(&code, "context ident", "end context;"), + context_token: code.keyword_token(Context, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), }) ) } diff --git a/vhdl_lang/src/syntax/declarative_part.rs b/vhdl_lang/src/syntax/declarative_part.rs index 731497c1..14362600 100644 --- a/vhdl_lang/src/syntax/declarative_part.rs +++ b/vhdl_lang/src/syntax/declarative_part.rs @@ -19,7 +19,7 @@ use crate::ast::{ContextClause, Declaration, PackageInstantiation}; use crate::data::DiagnosticHandler; pub fn parse_package_instantiation(stream: &mut TokenStream) -> ParseResult { - let package_token = stream.expect_kind(Package)?; + let package_token = stream.expect_kind(Package)?.into(); let ident = stream.expect_ident()?; stream.expect_kind(Is)?; stream.expect_kind(New)?; @@ -32,10 +32,10 @@ pub fn parse_package_instantiation(stream: &mut TokenStream) -> ParseResult { - (None, token) + (None, token.into()) } ); Ok(PackageInstantiation { @@ -43,7 +43,8 @@ pub fn parse_package_instantiation(stream: &mut TokenStream) -> ParseResult Pars } } } + pub fn parse_declarative_part( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, @@ -79,6 +81,18 @@ pub fn parse_declarative_part( Ok(decl) } +pub fn parse_declarative_part_end_token( + stream: &mut TokenStream, + diagnostics: &mut dyn DiagnosticHandler, + begin_is_end: bool, +) -> ParseResult<(Vec, Token)> { + let expected_end_token = if begin_is_end { Begin } else { End }; + let decl = parse_declarative_part_leave_end_token(stream, diagnostics)?; + let end_token = stream.peek_expect()?; + stream.expect_kind(expected_end_token).log(diagnostics); + Ok((decl, end_token)) +} + pub fn parse_declarative_part_leave_end_token( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, @@ -166,7 +180,7 @@ mod tests { use super::*; use crate::ast::{ObjectClass, ObjectDeclaration}; use crate::data::Diagnostic; - use crate::syntax::test::{source_range, Code}; + use crate::syntax::test::Code; #[test] fn package_instantiation() { @@ -182,7 +196,8 @@ package ident is new lib.foo.bar; ident: code.s1("ident").ident(), package_name: code.s1("lib.foo.bar").selected_name(), generic_map: None, - source_range: source_range(&code, "package", ";"), + package_token: code.keyword_token(Package, 1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -209,7 +224,8 @@ package ident is new lib.foo.bar )") .association_list() ), - source_range: source_range(&code, "package", ");"), + package_token: code.keyword_token(Package, 1), + semi_token: code.keyword_token(SemiColon, -1), } ); } diff --git a/vhdl_lang/src/syntax/design_unit.rs b/vhdl_lang/src/syntax/design_unit.rs index 630b06fc..2a7efbcc 100644 --- a/vhdl_lang/src/syntax/design_unit.rs +++ b/vhdl_lang/src/syntax/design_unit.rs @@ -9,15 +9,15 @@ 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_end_token, parse_declarative_part_leave_end_token, + parse_package_instantiation, }; -use super::interface_declaration::parse_generic_interface_list; use crate::ast::*; use crate::data::*; @@ -27,10 +27,10 @@ pub fn parse_entity_declaration( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - let entity_token = stream.expect_kind(Entity)?; + let entity_token = stream.expect_kind(Entity)?.into(); let ident = stream.expect_ident()?; - stream.expect_kind(Is)?; + let is_token = stream.expect_kind(Is)?.into(); let generic_clause = parse_optional_generic_list(stream, diagnostics)?; let port_clause = parse_optional_port_list(stream, diagnostics)?; @@ -38,17 +38,20 @@ pub fn parse_entity_declaration( let decl = parse_declarative_part_leave_end_token(stream, diagnostics)?; let token = stream.expect()?; - let statements = try_token_kind!( + let (begin_token, statements, end_token) = try_token_kind!( token, - End => Vec::new(), - Begin => parse_labeled_concurrent_statements(stream, diagnostics)? + End => (None, Vec::new(), token.into()), + Begin => { + let (statements, end_token) = parse_labeled_concurrent_statements_end_token(stream, diagnostics)?; + (Some(token.into()), statements, end_token.into()) + } ); 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); } - let semi_token = stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?.into(); Ok(EntityDeclaration { context_clause: ContextClause::default(), ident, @@ -56,7 +59,11 @@ pub fn parse_entity_declaration( port_clause, decl, statements, - source_range: entity_token.pos.combine_into(&semi_token), + entity_token, + is_token, + begin_token, + end_token, + semi_token, }) } @@ -65,15 +72,18 @@ pub fn parse_architecture_body( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - let architecture_token = stream.expect_kind(Architecture)?; + let architecture_token = stream.expect_kind(Architecture)?.into(); let ident = stream.expect_ident()?; stream.expect_kind(Of)?; let entity_name = stream.expect_ident()?; - stream.expect_kind(Is)?; + let is_token = stream.expect_kind(Is)?.into(); - let decl = parse_declarative_part(stream, diagnostics, true)?; + let (decl, begin_token) = parse_declarative_part_end_token(stream, diagnostics, true) + .map(|(decl, begin_token)| (decl, begin_token.into()))?; - let statements = parse_labeled_concurrent_statements(stream, diagnostics)?; + let (statements, end_token) = + parse_labeled_concurrent_statements_end_token(stream, diagnostics) + .map(|(statements, end_token)| (statements, end_token.into()))?; stream.pop_if_kind(Architecture)?; let end_ident = stream.pop_optional_ident()?; @@ -81,7 +91,7 @@ pub fn parse_architecture_body( diagnostics.push(diagnostic); } - let semi_token = stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?.into(); Ok(ArchitectureBody { context_clause: ContextClause::default(), @@ -89,7 +99,11 @@ pub fn parse_architecture_body( entity_name: entity_name.into_ref(), decl, statements, - source_range: architecture_token.pos.combine_into(&semi_token), + architecture_token, + is_token, + begin_token, + end_token, + semi_token, }) } @@ -98,33 +112,29 @@ pub fn parse_package_declaration( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - let package_token = stream.expect_kind(Package)?; + let package_token = stream.expect_kind(Package)?.into(); let ident = stream.expect_ident()?; - stream.expect_kind(Is)?; - let generic_clause = { - if stream.skip_if_kind(Generic)? { - let decl = parse_generic_interface_list(stream, diagnostics)?; - stream.expect_kind(SemiColon)?; - Some(decl) - } else { - None - } - }; - let decl = parse_declarative_part(stream, diagnostics, false)?; + let is_token = stream.expect_kind(Is)?.into(); + let generic_clause = parse_optional_generic_list(stream, diagnostics)?; + let (decl, end_token) = parse_declarative_part_end_token(stream, diagnostics, false) + .map(|(decl, end_token)| (decl, end_token.into()))?; 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)?; - let semi_token = stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?.into(); Ok(PackageDeclaration { context_clause: ContextClause::default(), ident, generic_clause, decl, - source_range: package_token.pos.combine_into(&semi_token), + package_token, + is_token, + end_token, + semi_token, }) } @@ -133,12 +143,13 @@ pub fn parse_package_body( stream: &mut TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { - let package_token = stream.expect_kind(Package)?; + let package_token = stream.expect_kind(Package)?.into(); 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)?.into(); + let (decl, end_token) = parse_declarative_part_end_token(stream, diagnostics, false) + .map(|(decl, end_token)| (decl, end_token.into()))?; if stream.skip_if_kind(Package)? { stream.expect_kind(Body)?; } @@ -146,13 +157,16 @@ pub fn parse_package_body( if let Some(diagnostic) = error_on_end_identifier_mismatch(&ident, &end_ident) { diagnostics.push(diagnostic); } - let semi_token = stream.expect_kind(SemiColon)?; + let semi_token = stream.expect_kind(SemiColon)?.into(); Ok(PackageBody { context_clause: ContextClause::default(), ident: ident.into_ref(), decl, - source_range: package_token.pos.combine_into(&semi_token), + package_token, + is_token, + end_token, + semi_token, }) } @@ -218,9 +232,6 @@ pub fn parse_design_file( }, Entity => match parse_entity_declaration(stream, diagnostics) { Ok(mut entity) => { - if let Some(ref context_item) = context_clause.first() { - entity.source_range = entity.source_range.combine_into(context_item) - } entity.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Primary(AnyPrimaryUnit::Entity(entity))); } @@ -229,9 +240,6 @@ pub fn parse_design_file( Architecture => match parse_architecture_body(stream, diagnostics) { Ok(mut architecture) => { - if let Some(ref context_item) = context_clause.first() { - architecture.source_range = architecture.source_range.combine_into(context_item) - } architecture.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Secondary(AnySecondaryUnit::Architecture(architecture))); } @@ -240,9 +248,6 @@ pub fn parse_design_file( Configuration => match parse_configuration_declaration(stream, diagnostics) { Ok(mut configuration) => { - if let Some(ref context_item) = context_clause.first() { - configuration.source_range = configuration.source_range.combine_into(context_item) - } configuration.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Primary(AnyPrimaryUnit::Configuration(configuration))); } @@ -252,9 +257,6 @@ pub fn parse_design_file( if stream.next_kinds_are(&[Package, Body])? { match parse_package_body(stream, diagnostics) { Ok(mut package_body) => { - if let Some(ref context_item) = context_clause.first() { - package_body.source_range = package_body.source_range.combine_into(context_item) - } package_body.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Secondary(AnySecondaryUnit::PackageBody(package_body))); } @@ -263,9 +265,6 @@ pub fn parse_design_file( } else if stream.next_kinds_are(&[Package, Identifier, Is, New])? { match parse_package_instantiation(stream) { Ok(mut inst) => { - if let Some(ref context_item) = context_clause.first() { - inst.source_range = inst.source_range.combine_into(context_item) - } inst.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Primary(AnyPrimaryUnit::PackageInstance(inst))) }, @@ -274,9 +273,6 @@ pub fn parse_design_file( } else { match parse_package_declaration(stream, diagnostics) { Ok(mut package) => { - if let Some(ref context_item) = context_clause.first() { - package.source_range = package.source_range.combine_into(context_item) - } package.context_clause = take_context_clause(&mut context_clause); design_units.push(AnyDesignUnit::Primary(AnyPrimaryUnit::Package(package))) } @@ -302,7 +298,7 @@ mod tests { use super::*; use crate::data::Diagnostic; - use crate::syntax::test::{check_diagnostics, check_no_diagnostics, source_range, Code}; + use crate::syntax::test::{check_diagnostics, check_no_diagnostics, Code}; use pretty_assertions::assert_eq; fn parse_str(code: &str) -> (Code, DesignFile, Vec) { @@ -332,15 +328,25 @@ mod tests { } /// An simple entity with only a name - fn simple_entity(ident: Ident, source_range: SrcPos) -> AnyDesignUnit { + fn simple_entity( + code: Code, + ident: &str, + entity_occurance: isize, + semi_occurance: isize, + ) -> AnyDesignUnit { + let ident = code.s1(ident).ident(); AnyDesignUnit::Primary(AnyPrimaryUnit::Entity(EntityDeclaration { context_clause: ContextClause::default(), - ident, + ident: ident.clone(), generic_clause: None, port_clause: None, decl: vec![], statements: vec![], - source_range, + entity_token: code.keyword_token(Entity, entity_occurance), + is_token: code.keyword_token(Is, semi_occurance), + begin_token: None, + end_token: code.keyword_token(End, semi_occurance), + semi_token: code.keyword_token(SemiColon, semi_occurance), })) } @@ -354,10 +360,7 @@ end entity; ); assert_eq!( design_file.design_units, - [simple_entity( - code.s1("myent").ident(), - source_range(&code, "entity", ";") - )] + [simple_entity(code, "myent", 1, 1)] ); let (code, design_file) = parse_ok( @@ -368,10 +371,7 @@ end entity myent; ); assert_eq!( design_file.design_units, - [simple_entity( - code.s1("myent").ident(), - source_range(&code, "entity", ";") - )] + [simple_entity(code, "myent", 1, 1)] ); } @@ -389,11 +389,19 @@ end entity; EntityDeclaration { context_clause: ContextClause::default(), ident: code.s1("myent").ident(), - generic_clause: Some(Vec::new()), + generic_clause: Some(InterfaceList { + items: Vec::new(), + start_token: code.keyword_token(Generic, 1), + semi_token: code.keyword_token(SemiColon, 1), + }), port_clause: None, decl: vec![], statements: vec![], - source_range: source_range(&code, "entity", "end entity;"), + entity_token: code.keyword_token(Entity, 1), + is_token: code.keyword_token(Is, 1), + begin_token: None, + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -417,11 +425,19 @@ end entity; item: code.symbol("myent"), pos: code.s1("myent").pos() }, - generic_clause: Some(vec![code.s1("runner_cfg : string").generic()]), + generic_clause: Some(InterfaceList { + items: vec![code.s1("runner_cfg : string").generic()], + start_token: code.keyword_token(Generic, 1), + semi_token: code.keyword_token(SemiColon, 1), + }), port_clause: None, decl: vec![], statements: vec![], - source_range: source_range(&code, "entity", "end entity;"), + entity_token: code.keyword_token(Entity, 1), + is_token: code.keyword_token(Is, 1), + begin_token: None, + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -441,10 +457,18 @@ end entity; context_clause: ContextClause::default(), ident: code.s1("myent").ident(), generic_clause: None, - port_clause: Some(vec![]), + port_clause: Some(InterfaceList { + items: vec![], + start_token: code.keyword_token(Port, 1), + semi_token: code.keyword_token(SemiColon, 1), + }), decl: vec![], statements: vec![], - source_range: source_range(&code, "entity", "end entity;"), + entity_token: code.keyword_token(Entity, 1), + is_token: code.keyword_token(Is, 1), + begin_token: None, + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -467,7 +491,11 @@ end entity; port_clause: None, decl: vec![], statements: vec![], - source_range: source_range(&code, "entity", "end entity;"), + entity_token: code.keyword_token(Entity, 1), + is_token: code.keyword_token(Is, 1), + begin_token: Some(code.keyword_token(Begin, 1)), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -490,7 +518,11 @@ end entity; port_clause: None, decl: code.s1("constant foo : natural := 0;").declarative_part(), statements: vec![], - source_range: source_range(&code, "entity", "end entity;"), + entity_token: code.keyword_token(Entity, 1), + is_token: code.keyword_token(Is, 1), + begin_token: None, + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -514,7 +546,11 @@ end entity; port_clause: None, decl: vec![], statements: vec![code.s1("check(clk, valid);").concurrent_statement()], - source_range: source_range(&code, "entity", "end entity;"), + entity_token: code.keyword_token(Entity, 1), + is_token: code.keyword_token(Is, 1), + begin_token: Some(code.keyword_token(Begin, 1)), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -539,39 +575,27 @@ end; assert_eq!( design_file.design_units, [ - simple_entity( - code.s1("myent").ident(), - source_range(&code, "entity myent", "end entity;") - ), - simple_entity( - code.s1("myent2").ident(), - source_range(&code, "entity myent2", "end entity myent2;") - ), - simple_entity( - code.s1("myent3").ident(), - source_range(&code, "entity myent3", "end myent3;") - ), - simple_entity( - code.s1("myent4").ident(), - source_range(&code, "entity myent4", "end;") - ) + simple_entity(code.clone(), "myent", 1, 1), + simple_entity(code.clone(), "myent2", 3, 2), + simple_entity(code.clone(), "myent3", 5, 3), + simple_entity(code.clone(), "myent4", 6, 4), ] ); } // An simple entity with only a name - fn simple_architecture( - ident: Ident, - entity_name: Ident, - source_range: SrcPos, - ) -> 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(), + ident: code.s1(ident).ident(), + entity_name: code.s1(entity_name).ident().into_ref(), decl: Vec::new(), statements: vec![], - source_range, + architecture_token: code.keyword_token(Architecture, 1), + is_token: code.keyword_token(Is, 1), + begin_token: code.keyword_token(Begin, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), })) } @@ -586,11 +610,7 @@ end architecture; ); assert_eq!( design_file.design_units, - [simple_architecture( - code.s1("arch_name").ident(), - code.s1("myent").ident(), - source_range(&code, "architecture", ";") - )] + [simple_architecture(code, "arch_name", "myent")] ); } @@ -605,11 +625,7 @@ end architecture arch_name; ); assert_eq!( design_file.design_units, - [simple_architecture( - code.s1("arch_name").ident(), - code.s1("myent").ident(), - source_range(&code, "architecture", ";") - )] + [simple_architecture(code, "arch_name", "myent")] ); } @@ -624,11 +640,7 @@ end; ); assert_eq!( design_file.design_units, - [simple_architecture( - code.s1("arch_name").ident(), - code.s1("myent").ident(), - source_range(&code, "architecture", ";") - )] + [simple_architecture(code, "arch_name", "myent")] ); } @@ -647,7 +659,10 @@ end package; ident: code.s1("pkg_name").ident(), generic_clause: None, decl: vec![], - source_range: source_range(&code, "package", ";"), + package_token: code.keyword_token(Package, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -674,7 +689,10 @@ end package; constant bar : natural := 0; ") .declarative_part(), - source_range: source_range(&code, "package", "end package;"), + package_token: code.keyword_token(Package, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -696,12 +714,16 @@ 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() - ]), + generic_clause: Some(InterfaceList { + items: vec![code.s1("type foo").generic(), code.s1("type bar").generic()], + start_token: code.keyword_token(Generic, 1), + semi_token: code.keyword_token(SemiColon, 2), + }), decl: vec![], - source_range: source_range(&code, "package", "end package;"), + package_token: code.keyword_token(Package, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ); } @@ -735,7 +757,11 @@ end entity; port_clause: None, decl: vec![], statements: vec![], - source_range: source_range(&code, "library", "end entity;"), + entity_token: code.keyword_token(Entity, 1), + is_token: code.keyword_token(Is, 1), + begin_token: None, + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ))] } @@ -772,7 +798,11 @@ end; entity_name: code.s1("myent").ident().into_ref(), decl: Vec::new(), statements: vec![], - source_range: source_range(&code, "library", "end;"), + architecture_token: code.keyword_token(Architecture, 1), + is_token: code.keyword_token(Is, 1), + begin_token: code.keyword_token(Begin, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ))], } @@ -806,8 +836,11 @@ end; ], ident: code.s1("pkg_name").ident(), generic_clause: None, - decl: vec![], - source_range: source_range(&code, "library", "end;"), + decl: Vec::new(), + package_token: code.keyword_token(Package, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ))], } @@ -840,8 +873,11 @@ end; .map_into(ContextItem::Use), ], ident: code.s1("pkg_name").ident().into_ref(), - decl: vec![], - source_range: source_range(&code, "library", "end;"), + decl: Vec::new(), + package_token: code.keyword_token(Package, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ))], } @@ -883,7 +919,10 @@ end; use_clauses: vec![], items: vec![], }, - source_range: source_range(&code, "library", "end;") + configuration_token: code.keyword_token(Configuration, 1), + is_token: code.keyword_token(Is, 1), + end_token: code.keyword_token(End, -1), + semi_token: code.keyword_token(SemiColon, -1), } ))], } @@ -916,7 +955,8 @@ package ident is new lib.foo.bar; ident: code.s1("ident").ident(), package_name: code.s1("lib.foo.bar").selected_name(), generic_map: None, - source_range: source_range(&code, "library", "bar;"), + package_token: code.keyword_token(Package, 1), + semi_token: code.keyword_token(SemiColon, -1), } ))], } diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index 098cede5..adab2946 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -16,7 +16,7 @@ use super::range::{parse_discrete_range, parse_range}; use super::sequential_statement::parse_sequential_statement; use super::subprogram::{parse_signature, parse_subprogram_declaration_no_semi}; use super::subtype_indication::parse_subtype_indication; -use super::tokens::{Comment, Symbols, Token, TokenStream, Tokenizer}; +use super::tokens::{Comment, KeyWordToken, Kind, Symbols, Token, TokenStream, Tokenizer}; use super::waveform::parse_waveform; use crate::ast; use crate::ast::*; @@ -425,6 +425,29 @@ impl Code { name => panic!("Expected attribute got {:?}", name), } } + + /// Get a keyword token from the n:th occurance of kind or last occurance if + /// occurance = -1 + pub fn keyword_token(&self, kind: Kind, occurance: isize) -> KeyWordToken { + let mut count = 0; + let mut keyword_token = None; + for token in self.tokenize().iter() { + if token.kind == kind { + count += 1; + if occurance == -1 || count == occurance { + keyword_token = Some(KeyWordToken { + kind: token.kind, + pos: token.pos.clone(), + comments: token.comments.clone(), + }); + if count == occurance { + break; + } + } + } + } + keyword_token.unwrap() + } } fn substr_range(source: &Source, range: Range, substr: &str, occurence: usize) -> Range { @@ -569,24 +592,47 @@ impl AsRef for Code { } } -// Create a Range spanning from the first substring occurance of start to -// to the first occurance of end after start (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"); - } - } -} +// // Create a Range spanning from the first substring occurance of start to +// // to the first occurance of end after start (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_lang/src/syntax/tokens/tokenizer.rs b/vhdl_lang/src/syntax/tokens/tokenizer.rs index 5ea9ce91..6fda403b 100644 --- a/vhdl_lang/src/syntax/tokens/tokenizer.rs +++ b/vhdl_lang/src/syntax/tokens/tokenizer.rs @@ -406,6 +406,13 @@ pub struct Token { pub comments: Option>, } +#[derive(PartialEq, Clone, Debug)] +pub struct KeyWordToken { + pub kind: Kind, + pub pos: SrcPos, + pub comments: Option>, +} + #[derive(PartialEq, Clone, Debug)] pub struct TokenComments { pub leading: Vec, @@ -433,6 +440,16 @@ impl Into for Token { } } +impl From for KeyWordToken { + fn from(token: Token) -> KeyWordToken { + KeyWordToken { + kind: token.kind, + pos: token.pos, + comments: token.comments, + } + } +} + pub fn kinds_error>(pos: T, kinds: &[Kind]) -> Diagnostic { Diagnostic::error( pos.as_ref(), diff --git a/vhdl_ls/src/document_symbol.rs b/vhdl_ls/src/document_symbol.rs index 2adf21b8..d80ab9c4 100644 --- a/vhdl_ls/src/document_symbol.rs +++ b/vhdl_ls/src/document_symbol.rs @@ -6,6 +6,7 @@ use lsp_types::{DocumentSymbol, DocumentSymbolResponse, SymbolKind, Url}; use vhdl_lang::ast::*; +use vhdl_lang::Latin1String; use vhdl_lang::{Source, VHDLParser}; pub fn nested_document_symbol_response_from_file(uri: &Url) -> Option { @@ -35,21 +36,7 @@ pub fn nested_document_symbol_response(design_file: &DesignFile) -> DocumentSymb DocumentSymbolResponse::from(response) } -fn symbol_kind_from_entity_class(entity_class: EntityClass) -> SymbolKind { - match entity_class { - EntityClass::Entity => SymbolKind::Interface, - EntityClass::Architecture => SymbolKind::Class, - EntityClass::Configuration => SymbolKind::Constructor, - EntityClass::Package => SymbolKind::Package, - _ => SymbolKind::Unknown, - } -} - -pub trait HasSymbolKind { - fn symbol_kind(&self) -> SymbolKind; -} - -trait HasDocumentSymbol { +pub trait HasDocumentSymbol { fn document_symbol(&self) -> DocumentSymbol; } @@ -74,158 +61,895 @@ impl HasDocumentSymbol for AnyPrimaryUnit { } } -impl HasSymbolKind for EntityDeclaration { - fn symbol_kind(&self) -> SymbolKind { - symbol_kind_from_entity_class(EntityClass::Entity) +impl HasDocumentSymbol for EntityDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(self.context_clause.clone(), &mut children); + push_generic_interface_list(self.generic_clause.as_ref(), &mut children); + push_port_interface_list(self.port_clause.as_ref(), &mut children); + let decl_start = if let Some(ref ports) = self.port_clause { + ports.semi_token.clone() + } else if let Some(ref generics) = self.generic_clause { + generics.semi_token.clone() + } else { + self.is_token.clone() + }; + let decl_end = if let Some(ref begin) = self.begin_token { + begin.clone() + } else { + self.end_token.clone() + }; + if !self.decl.is_empty() { + push_declarations(&decl_start, &decl_end, &self.decl, &mut children); + } + if let Some(ref begin) = self.begin_token { + push_concurrent_statement_part(begin, &self.end_token, &self.statements, &mut children); + } + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("entity")), + kind: SymbolKind::Interface, + deprecated: None, + range: lsp_types::Range { + start: to_lsp_pos( + self.context_clause + .first() + .map_or(self.entity_token.pos.start(), |first| first.pos.start()), + ), + end: to_lsp_pos(self.semi_token.pos.end()), + }, + selection_range: to_lsp_range(self.ident.pos.range()), + children: none_if_empty(children), + } } } -impl HasDocumentSymbol for EntityDeclaration { +impl HasDocumentSymbol for ConfigurationDeclaration { fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(self.context_clause.clone(), &mut children); DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(String::from("entity")), - kind: self.symbol_kind(), + detail: Some(String::from("configuration")), + kind: SymbolKind::Constructor, deprecated: None, - range: to_lsp_range(self.source_range.range()), + range: lsp_types::Range { + start: to_lsp_pos( + self.context_clause + .first() + .map_or(self.configuration_token.pos.start(), |first| { + first.pos.start() + }), + ), + end: to_lsp_pos(self.semi_token.pos.end()), + }, selection_range: to_lsp_range(self.ident.pos.range()), - children: None, + children: none_if_empty(children), + } + } +} + +impl HasDocumentSymbol for PackageDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(self.context_clause.clone(), &mut children); + push_generic_interface_list(self.generic_clause.as_ref(), &mut children); + push_declarations( + self.generic_clause + .as_ref() + .map_or(&self.is_token, |generics| &generics.semi_token), + &self.end_token, + &self.decl, + &mut children, + ); + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("package")), + kind: SymbolKind::Package, + deprecated: None, + range: lsp_types::Range { + start: to_lsp_pos( + self.context_clause + .first() + .map_or(self.package_token.pos.start(), |first| first.pos.start()), + ), + end: to_lsp_pos(self.semi_token.pos.end()), + }, + selection_range: to_lsp_range(self.ident.pos.range()), + children: none_if_empty(children), } } } -impl HasSymbolKind for ConfigurationDeclaration { - fn symbol_kind(&self) -> SymbolKind { - symbol_kind_from_entity_class(EntityClass::Configuration) +impl HasDocumentSymbol for PackageInstantiation { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(self.context_clause.clone(), &mut children); + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("package instance")), + kind: SymbolKind::Package, + deprecated: None, + range: lsp_types::Range { + start: to_lsp_pos( + self.context_clause + .first() + .map_or(self.package_token.pos.start(), |first| first.pos.start()), + ), + end: to_lsp_pos(self.semi_token.pos.end()), + }, + selection_range: to_lsp_range(self.ident.pos.range()), + children: none_if_empty(children), + } } } -impl HasDocumentSymbol for ConfigurationDeclaration { +impl HasDocumentSymbol for ContextDeclaration { fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(self.items.clone(), &mut children); DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(String::from("configuration")), - kind: self.symbol_kind(), + detail: Some(String::from("context")), + kind: SymbolKind::Namespace, deprecated: None, - range: to_lsp_range(self.source_range.range()), + range: lsp_range(&self.context_token, &self.semi_token), selection_range: to_lsp_range(self.ident.pos.range()), - children: None, + children: none_if_empty(children), } } } -impl HasSymbolKind for PackageDeclaration { - fn symbol_kind(&self) -> SymbolKind { - symbol_kind_from_entity_class(EntityClass::Package) +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 PackageDeclaration { +impl HasDocumentSymbol for PackageBody { fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(self.context_clause.clone(), &mut children); + push_declarations(&self.is_token, &self.end_token, &self.decl, &mut children); + DocumentSymbol { + name: self.ident.item.item.name_utf8(), + detail: Some(String::from("package body")), + kind: SymbolKind::Package, + deprecated: None, + range: lsp_types::Range { + start: to_lsp_pos( + self.context_clause + .first() + .map_or(self.package_token.pos.start(), |first| first.pos.start()), + ), + end: to_lsp_pos(self.semi_token.pos.end()), + }, + selection_range: to_lsp_range(self.ident.item.pos.range()), + children: none_if_empty(children), + } + } +} + +impl HasDocumentSymbol for ArchitectureBody { + fn document_symbol(&self) -> DocumentSymbol { + let mut children = vec![]; + push_context_clause(self.context_clause.clone(), &mut children); + push_declarations(&self.is_token, &self.begin_token, &self.decl, &mut children); + push_concurrent_statement_part( + &self.begin_token, + &self.end_token, + &self.statements, + &mut children, + ); DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(String::from("package")), - kind: self.symbol_kind(), + detail: Some(format!( + "architecture of {}", + self.entity_name.item.item.name().to_string() + )), + kind: SymbolKind::Class, deprecated: None, - range: to_lsp_range(self.source_range.range()), + range: lsp_types::Range { + start: to_lsp_pos( + self.context_clause + .first() + .map_or(self.architecture_token.pos.start(), |first| { + first.pos.start() + }), + ), + end: to_lsp_pos(self.semi_token.pos.end()), + }, selection_range: to_lsp_range(self.ident.pos.range()), - children: None, + children: none_if_empty(children), } } } -impl HasSymbolKind for PackageInstantiation { - fn symbol_kind(&self) -> SymbolKind { - symbol_kind_from_entity_class(EntityClass::Package) +impl HasDocumentSymbol for ContextClause { + fn document_symbol(&self) -> DocumentSymbol { + let (range, selection_range, children) = { + if let Some(first) = self.first() { + ( + to_lsp_range(first.pos.combine(&self.last().unwrap().pos).range()), + to_lsp_range(first.pos.range()), + { + let mut children = vec![]; + for child in self.iter() { + children.push(child.item.document_symbol()); + } + Some(children) + }, + ) + } else { + (NULL_RANGE, NULL_RANGE, None) + } + }; + DocumentSymbol { + name: String::from("context"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range, + selection_range, + children, + } } } -impl HasDocumentSymbol for PackageInstantiation { +impl HasDocumentSymbol for ContextItem { + fn document_symbol(&self) -> DocumentSymbol { + match self { + ContextItem::Use(use_clause) => use_clause.document_symbol(), + ContextItem::Library(library_clause) => library_clause.document_symbol(), + ContextItem::Context(context_reference) => context_reference.document_symbol(), + } + } +} + +impl HasDocumentSymbol for UseClause { + fn document_symbol(&self) -> DocumentSymbol { + if self.name_list.is_empty() { + DocumentSymbol { + name: String::from("use"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: NULL_RANGE, + selection_range: NULL_RANGE, + children: None, + } + } else if self.name_list.len() == 1 { + let name = self.name_list.first().unwrap(); + DocumentSymbol { + name: name_to_string(&name.item), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(name.pos.range()), + selection_range: to_lsp_range(name.pos.range()), + children: None, + } + } else { + let first = self.name_list.first().unwrap(); + let last = self.name_list.last().unwrap(); + DocumentSymbol { + name: String::from("use"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(first.pos.combine(&last.pos).range()), + selection_range: to_lsp_range(first.pos.range()), + children: Some( + self.name_list + .iter() + .map(|name| DocumentSymbol { + name: name_to_string(&name.item), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(name.pos.range()), + selection_range: to_lsp_range(name.pos.range()), + children: None, + }) + .collect(), + ), + } + } + } +} + +impl HasDocumentSymbol for LibraryClause { + fn document_symbol(&self) -> DocumentSymbol { + if self.name_list.is_empty() { + DocumentSymbol { + name: String::from("library"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: NULL_RANGE, + selection_range: NULL_RANGE, + children: None, + } + } else if self.name_list.len() == 1 { + let name = self.name_list.first().unwrap(); + DocumentSymbol { + name: name.item.name_utf8(), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(name.pos.range()), + selection_range: to_lsp_range(name.pos.range()), + children: None, + } + } else { + let first = self.name_list.first().unwrap(); + let last = self.name_list.last().unwrap(); + DocumentSymbol { + name: String::from("library"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(first.pos.combine(&last.pos).range()), + selection_range: to_lsp_range(first.pos.range()), + children: Some( + self.name_list + .iter() + .map(|name| DocumentSymbol { + name: name.item.name_utf8(), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(name.pos.range()), + selection_range: to_lsp_range(name.pos.range()), + children: None, + }) + .collect(), + ), + } + } + } +} + +impl HasDocumentSymbol for ContextReference { + fn document_symbol(&self) -> DocumentSymbol { + if self.name_list.is_empty() { + DocumentSymbol { + name: String::from("context"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: NULL_RANGE, + selection_range: NULL_RANGE, + children: None, + } + } else if self.name_list.len() == 1 { + let name = self.name_list.first().unwrap(); + DocumentSymbol { + name: name_to_string(&name.item), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(name.pos.range()), + selection_range: to_lsp_range(name.pos.range()), + children: None, + } + } else { + let first = self.name_list.first().unwrap(); + let last = self.name_list.last().unwrap(); + DocumentSymbol { + name: String::from("context"), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(first.pos.combine(&last.pos).range()), + selection_range: to_lsp_range(first.pos.range()), + children: Some( + self.name_list + .iter() + .map(|name| DocumentSymbol { + name: name_to_string(&name.item), + detail: None, + kind: SymbolKind::Namespace, + deprecated: None, + range: to_lsp_range(name.pos.range()), + selection_range: to_lsp_range(name.pos.range()), + children: None, + }) + .collect(), + ), + } + } + } +} + +impl HasDocumentSymbol for InterfaceList { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: String::from("interface list"), + detail: None, + kind: SymbolKind::Unknown, + deprecated: None, + range: lsp_range(&self.start_token, &self.semi_token), + selection_range: lsp_token_range(&self.start_token), + children: Some( + self.items + .iter() + .map(|item| item.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.range()), + selection_range: to_lsp_range(ident.pos.range()), + 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 { DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(String::from("package instance")), - kind: self.symbol_kind(), + detail: { + match self.class { + ObjectClass::Constant => None, + _ => Some(format!(": {}", mode_to_string(self.mode))), + } + }, + kind: symbol_kind_from_object_class(self.class), deprecated: None, - range: to_lsp_range(self.source_range.range()), + range: to_lsp_range(self.ident.pos.range()), selection_range: to_lsp_range(self.ident.pos.range()), children: None, } } } -impl HasSymbolKind for ContextDeclaration { - fn symbol_kind(&self) -> SymbolKind { - SymbolKind::Namespace +impl HasDocumentSymbol for InterfaceFileDeclaration { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: self.ident.item.name_utf8(), + detail: Some(String::from("File")), + kind: SymbolKind::File, + deprecated: None, + range: to_lsp_range(self.ident.pos.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } } } -impl HasDocumentSymbol for ContextDeclaration { +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.range()), + selection_range: to_lsp_range(procedure.designator.pos.range()), + 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.range()), + selection_range: to_lsp_range(function.designator.pos.range()), + children: None, + }, + } + } +} + +impl HasDocumentSymbol for InterfacePackageDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(String::from("context")), - kind: self.symbol_kind(), + detail: Some(String::from("Package")), + kind: SymbolKind::Package, deprecated: None, - range: to_lsp_range(self.source_range.range()), + range: to_lsp_range(self.ident.pos.range()), selection_range: to_lsp_range(self.ident.pos.range()), children: None, } } } -impl HasDocumentSymbol for AnySecondaryUnit { +impl HasDocumentSymbol for Declaration { fn document_symbol(&self) -> DocumentSymbol { match self { - AnySecondaryUnit::PackageBody(ref unit) => unit.document_symbol(), - AnySecondaryUnit::Architecture(ref unit) => unit.document_symbol(), + 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.item.document_symbol(), //WithPos + Declaration::Package(package) => package.document_symbol(), + Declaration::Configuration(configuration) => configuration.document_symbol(), } } } -impl HasSymbolKind for PackageBody { - fn symbol_kind(&self) -> SymbolKind { - symbol_kind_from_entity_class(EntityClass::Package) +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.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } } } -impl HasDocumentSymbol for PackageBody { +impl HasDocumentSymbol for FileDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { - name: self.ident.item.item.name_utf8(), - detail: Some(String::from("package body")), - kind: self.symbol_kind(), + name: self.ident.item.name_utf8(), + detail: None, + kind: SymbolKind::File, deprecated: None, - range: to_lsp_range(self.source_range.range()), - selection_range: to_lsp_range(self.ident.item.pos.range()), + range: to_lsp_range(self.ident.pos.range()), + selection_range: to_lsp_range(self.ident.pos.range()), children: None, } } } -impl HasSymbolKind for ArchitectureBody { - fn symbol_kind(&self) -> SymbolKind { - symbol_kind_from_entity_class(EntityClass::Architecture) +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.range()), + selection_range: to_lsp_range(self.ident.pos.range()), + children: None, + } } } -impl HasDocumentSymbol for ArchitectureBody { +impl HasDocumentSymbol for ComponentDeclaration { fn document_symbol(&self) -> DocumentSymbol { DocumentSymbol { name: self.ident.item.name_utf8(), - detail: Some(format!( - "architecture of {}", - self.entity_name.item.item.name().to_string() - )), - kind: self.symbol_kind(), + detail: Some(String::from("component")), + kind: SymbolKind::Interface, deprecated: None, - range: to_lsp_range(self.source_range.range()), + range: to_lsp_range(self.ident.pos.range()), selection_range: to_lsp_range(self.ident.pos.range()), 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.range()), + selection_range: to_lsp_range(spec.ident.pos.range()), + 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.range()), + selection_range: to_lsp_range(decl.ident.pos.range()), + 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::Interface, + deprecated: None, + range: to_lsp_range(self.designator.pos.range()), + selection_range: to_lsp_range(self.designator.pos.range()), + 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.range()), + selection_range: to_lsp_range(self.spec.component_name.pos.range()), + children: None, + } + } +} + +impl HasDocumentSymbol for LabeledConcurrentStatement { + fn document_symbol(&self) -> DocumentSymbol { + let mut symbol = match &self.statement { + 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(), + }; + 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.range()); + } + symbol + } +} + +impl HasDocumentSymbol for ConcurrentProcedureCall { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: String::from("procedure"), + detail: None, + kind: SymbolKind::Method, + deprecated: None, + range: to_lsp_range(self.call.name.pos.range()), + selection_range: to_lsp_range(self.call.name.pos.range()), + children: None, + } + } +} + +impl HasDocumentSymbol for BlockStatement { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: String::from("block"), + detail: None, + kind: SymbolKind::Module, + deprecated: None, + range: lsp_range(&self.block_token, &self.semi_token), + selection_range: lsp_token_range(&self.block_token), + children: None, + } + } +} + +impl HasDocumentSymbol for ProcessStatement { + fn document_symbol(&self) -> DocumentSymbol { + DocumentSymbol { + name: String::from("process"), + detail: None, + kind: SymbolKind::Event, + deprecated: None, + range: lsp_range(&self.start_token, &self.semi_token), + selection_range: lsp_token_range(&self.start_token), + 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.range()), + selection_range: to_lsp_range(self.statement.condition.pos.range()), + 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.range()), + selection_range: to_lsp_range(self.target.pos.range()), + 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::Class, + deprecated: None, + range: to_lsp_range(selected_name.pos.range()), + selection_range: to_lsp_range(selected_name.pos.range()), + children: None, + }, + InstantiatedUnit::Entity(selected_name, _) => DocumentSymbol { + name: String::from("entity"), + detail: None, + kind: SymbolKind::Class, + deprecated: None, + range: to_lsp_range(selected_name.pos.range()), + selection_range: to_lsp_range(selected_name.pos.range()), + children: None, + }, + InstantiatedUnit::Configuration(selected_name) => DocumentSymbol { + name: String::from("configuration"), + detail: None, + kind: SymbolKind::Class, + deprecated: None, + range: to_lsp_range(selected_name.pos.range()), + selection_range: to_lsp_range(selected_name.pos.range()), + 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.range()), + selection_range: to_lsp_range(self.index_name.pos.range()), + 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.range()) + } else { + NULL_RANGE + }; + 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.range()); + 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", + }) +} + +const NULL_RANGE: lsp_types::Range = lsp_types::Range { + start: lsp_types::Position { + line: 0, + character: 0, + }, + end: lsp_types::Position { + line: 0, + character: 0, + }, +}; + fn to_lsp_pos(position: vhdl_lang::Position) -> lsp_types::Position { lsp_types::Position { line: position.line as u64, @@ -240,11 +964,123 @@ fn to_lsp_range(range: vhdl_lang::Range) -> lsp_types::Range { } } +fn lsp_range(from: &KeyWordToken, to: &KeyWordToken) -> lsp_types::Range { + lsp_types::Range { + start: to_lsp_pos(from.pos.start()), + end: to_lsp_pos(to.pos.end()), + } +} + +fn lsp_token_range(token: &KeyWordToken) -> lsp_types::Range { + lsp_types::Range { + start: to_lsp_pos(token.pos.start()), + end: to_lsp_pos(token.pos.end()), + } +} + +fn lsp_range_between(from: &KeyWordToken, to: &KeyWordToken) -> lsp_types::Range { + lsp_types::Range { + start: to_lsp_pos(from.pos.end()), + end: to_lsp_pos(to.pos.start()), + } +} + +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 !context_clause.is_empty() { + symbols.push(context_clause.document_symbol()); + } +} + +fn push_generic_interface_list( + interface_list: Option<&InterfaceList>, + symbols: &mut Vec, +) { + push_interface_list(interface_list, symbols, "generics", SymbolKind::Constant); +} + +fn push_port_interface_list( + interface_list: Option<&InterfaceList>, + symbols: &mut Vec, +) { + push_interface_list(interface_list, symbols, "ports", SymbolKind::Field); +} + +fn push_interface_list( + interface_list: Option<&InterfaceList>, + symbols: &mut Vec, + name: &str, + symbol_kind: SymbolKind, +) { + if let Some(ref list) = interface_list { + let mut symbol = list.document_symbol(); + symbol.name = String::from(name); + symbol.kind = symbol_kind; + symbols.push(symbol); + } +} + +fn push_declarations( + start_token: &KeyWordToken, + end_token: &KeyWordToken, + decl: &[Declaration], + symbols: &mut Vec, +) { + symbols.push(DocumentSymbol { + name: String::from("declarations"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: lsp_range_between(start_token, end_token), + selection_range: lsp_range_between(start_token, end_token), + children: Some(decl.iter().map(|decl| decl.document_symbol()).collect()), + }); +} + +fn push_concurrent_statement_part( + start_token: &KeyWordToken, + end_token: &KeyWordToken, + statements: &[LabeledConcurrentStatement], + symbols: &mut Vec, +) { + symbols.push(DocumentSymbol { + name: String::from("statements"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: lsp_range_between(start_token, end_token), + selection_range: lsp_range_between(start_token, end_token), + children: Some( + statements + .iter() + .map(|decl| decl.document_symbol()) + .collect(), + ), + }); +} + #[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; @@ -264,15 +1100,6 @@ mod tests { design_file } - fn range(start: (u64, u64), end: (u64, u64)) -> lsp_types::Range { - let (start_line, start_character) = start; - let (end_line, end_character) = end; - lsp_types::Range { - start: lsp_types::Position::new(start_line, start_character), - end: lsp_types::Position::new(end_line, end_character), - } - } - fn write_source_file(code: &str) -> (Url, NamedTempFile) { let mut file = NamedTempFile::new().unwrap(); file.write_all(code.as_bytes()).unwrap(); @@ -282,213 +1109,385 @@ mod tests { ) } + 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: None, + 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: range1(code, "generic"), + children: Some(vec![DocumentSymbol { + name: String::from("g1"), + detail: None, + 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::Field, + deprecated: None, + range: range(code, "port(", ");"), + selection_range: range1(code, "port"), + children: Some(vec![DocumentSymbol { + name: String::from("p1"), + detail: Some(String::from(": in")), + 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" }; + DocumentSymbol { + name: String::from("declarations"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: find_range(code, start, start_occurance, end, false), + selection_range: find_range(code, start, start_occurance, end, false), + 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" }; + DocumentSymbol { + name: String::from("statements"), + detail: None, + kind: SymbolKind::Field, + deprecated: None, + range: range_between(code, start, "end"), + selection_range: range_between(code, start, "end"), + 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 design_file = parse_str( - " + let code = " library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; entity ent1 is + generic( + g1 : integer := 3 + ); + port( + p1 : integer + ); + signal decl1 : integer; end; -", - ); - assert_eq!(design_file.design_units.len(), 1); - let entity = match design_file.design_units.first().unwrap() { - AnyDesignUnit::Primary(primary) => match primary { - AnyPrimaryUnit::Entity(entity) => entity, - _ => panic!("expected entity"), - }, - _ => panic!("expected entity"), - }; - +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); assert_eq!( - entity.document_symbol(), + unit.document_symbol(), DocumentSymbol { name: String::from("ent1"), detail: Some(String::from("entity")), kind: SymbolKind::Interface, deprecated: None, - range: range((1, 0), (6, 4)), - selection_range: range((5, 7), (5, 11)), - children: 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, false), + ]), } ); } #[test] fn configuration_declaration() { - let design_file = parse_str( - " + let code = " library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - 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!( - design_file.design_units.first().unwrap().document_symbol(), + unit.document_symbol(), DocumentSymbol { name: String::from("cfg"), detail: Some(String::from("configuration")), kind: SymbolKind::Constructor, deprecated: None, - range: range((1, 0), (8, 4)), - selection_range: range((5, 14), (5, 17)), - children: None, + range: range(code, "library", "end;"), + selection_range: range1(code, "cfg"), + children: Some(vec![ieee_context(code)]), } ); } #[test] fn package_declaration() { - let design_file = parse_str( - " + let code = " library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - 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!( - design_file.design_units.first().unwrap().document_symbol(), + unit.document_symbol(), DocumentSymbol { name: String::from("pkg"), detail: Some(String::from("package")), kind: SymbolKind::Package, deprecated: None, - range: range((1, 0), (6, 4)), - selection_range: range((5, 8), (5, 11)), - children: 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 design_file = parse_str( - " + let code = " library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - 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!( - design_file.design_units.first().unwrap().document_symbol(), + unit.document_symbol(), DocumentSymbol { name: String::from("pkg_inst"), detail: Some(String::from("package instance")), kind: SymbolKind::Package, deprecated: None, - range: range((1, 0), (8, 6)), - selection_range: range((5, 8), (5, 16)), - children: None, + range: range(code, "library", ");"), + selection_range: range1(code, "pkg_inst"), + children: Some(vec![ieee_context(code)]), } ); } #[test] fn context_declaration() { - let design_file = parse_str( - " + let code = " context ctx is library ieee; - use ieee.std_logic_1164.all; - use ieee.numeric_std.all; end; -", - ); +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); assert_eq!( - design_file.design_units.first().unwrap().document_symbol(), + unit.document_symbol(), DocumentSymbol { name: String::from("ctx"), detail: Some(String::from("context")), kind: SymbolKind::Namespace, deprecated: None, - range: range((1, 0), (5, 4)), - selection_range: range((1, 8), (1, 11)), - children: None, + range: range(code, "context", "end;"), + selection_range: range1(code, "ctx"), + children: Some(vec![ieee_context(code)]), } ); } #[test] fn package_body() { - let design_file = parse_str( - " + let code = " library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - package body pkg is + signal decl1 : integer; end; -", - ); +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); assert_eq!( - design_file.design_units.first().unwrap().document_symbol(), + unit.document_symbol(), DocumentSymbol { name: String::from("pkg"), detail: Some(String::from("package body")), kind: SymbolKind::Package, deprecated: None, - range: range((1, 0), (6, 4)), - selection_range: range((5, 13), (5, 16)), - children: 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 design_file = parse_str( - " + let code = " library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - architecture rtl of ent is + signal decl1 : integer; begin + stmt1: decl1 <= 1; end; -", - ); +"; + let design_file = parse_str(code); + let unit = design_file.design_units.first().unwrap(); assert_eq!( - design_file.design_units.first().unwrap().document_symbol(), + unit.document_symbol(), DocumentSymbol { name: String::from("rtl"), detail: Some(String::from("architecture of ent")), kind: SymbolKind::Class, deprecated: None, - range: range((1, 0), (7, 4)), - selection_range: range((5, 13), (5, 16)), - children: None, + range: range(code, "library", "end;"), + selection_range: range1(code, "rtl"), + children: Some(vec![ + ieee_context(code), + simple_declaration(code, "is", 1, true), + simple_statement(code, true) + ]), } ); } #[test] fn test_nested_document_symbol_response_from_file() { - let (source_url, _file) = write_source_file( - " + let code = " library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; entity ent1 is -end; -", - ); +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 { @@ -496,9 +1495,9 @@ end; detail: Some(String::from("entity")), kind: SymbolKind::Interface, deprecated: None, - range: range((1, 0), (6, 4)), - selection_range: range((5, 7), (5, 11)), - children: None, + range: range(code, "library", "end entity;"), + selection_range: range1(code, "ent1"), + children: Some(vec![ieee_context(code)]), }]) ); }