diff --git a/vhdl_lang/src/analysis/root.rs b/vhdl_lang/src/analysis/root.rs index 2ce49a666..b0290e9f1 100644 --- a/vhdl_lang/src/analysis/root.rs +++ b/vhdl_lang/src/analysis/root.rs @@ -317,6 +317,12 @@ impl DesignRoot { ItemAtCursor::search(self, source, cursor) } + /// Search for the declaration at decl_pos and format it + pub fn format_declaration(&self, decl_pos: &SrcPos) -> Option { + FormatDeclaration::search(self, decl_pos) + } + + /// Search for all references to the declaration at decl_pos pub fn find_all_references(&self, decl_pos: &SrcPos) -> Vec { FindAllReferences::search(self, decl_pos) } diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index 034b1df26..402f8acd7 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -515,6 +515,13 @@ pub enum ObjectClass { SharedVariable, } +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum InterfaceListType { + Port, + Generic, + Parameter, +} + #[derive(PartialEq, Debug, Clone)] pub struct ObjectDeclaration { pub class: ObjectClass, @@ -583,6 +590,7 @@ pub struct InterfaceFileDeclaration { /// LRM 6.5.2 Interface object declarations #[derive(PartialEq, Debug, Clone)] pub struct InterfaceObjectDeclaration { + pub list_type: InterfaceListType, pub class: ObjectClass, pub ident: Ident, pub mode: Mode, @@ -1105,6 +1113,7 @@ pub struct EntityDeclaration { pub decl: Vec, pub statements: Vec, } + /// LRM 3.3 Architecture bodies #[derive(PartialEq, Debug, Clone)] pub struct ArchitectureBody { diff --git a/vhdl_lang/src/ast/display.rs b/vhdl_lang/src/ast/display.rs index c393197f4..1bedb75a8 100644 --- a/vhdl_lang/src/ast/display.rs +++ b/vhdl_lang/src/ast/display.rs @@ -9,6 +9,591 @@ use super::*; use std::fmt::{Display, Formatter, Result}; +impl Display for WithPos { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", &self.item) + } +} + +impl Display for BaseSpecifier { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + BaseSpecifier::B => write!(f, "b"), + BaseSpecifier::O => write!(f, "o"), + BaseSpecifier::X => write!(f, "x"), + BaseSpecifier::UB => write!(f, "ub"), + BaseSpecifier::UO => write!(f, "uo"), + BaseSpecifier::UX => write!(f, "ux"), + BaseSpecifier::SB => write!(f, "sb"), + BaseSpecifier::SO => write!(f, "so"), + BaseSpecifier::SX => write!(f, "sx"), + BaseSpecifier::D => write!(f, "d"), + } + } +} + +impl Display for Unary { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Unary::And => write!(f, "and "), + Unary::Or => write!(f, "or "), + Unary::Nand => write!(f, "nand "), + Unary::Nor => write!(f, "nor "), + Unary::Xor => write!(f, "xor "), + Unary::Xnor => write!(f, "xnor "), + Unary::Abs => write!(f, "abs "), + Unary::Not => write!(f, "not "), + Unary::Minus => write!(f, "-"), + Unary::Plus => write!(f, "+"), + Unary::QueQue => write!(f, "?? "), + } + } +} + +impl Display for Binary { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Binary::And => write!(f, " and "), + Binary::Or => write!(f, " or "), + Binary::Nand => write!(f, " nand "), + Binary::Nor => write!(f, " nor "), + Binary::Xor => write!(f, " xor "), + Binary::Xnor => write!(f, " xnor "), + Binary::EQ => write!(f, " = "), + Binary::NE => write!(f, " /= "), + Binary::LT => write!(f, " < "), + Binary::LTE => write!(f, " <= "), + Binary::GT => write!(f, " > "), + Binary::GTE => write!(f, " >= "), + Binary::QueEQ => write!(f, " ?= "), + Binary::QueNE => write!(f, " ?/= "), + Binary::QueLT => write!(f, " ?< "), + Binary::QueLTE => write!(f, " ?<= "), + Binary::QueGT => write!(f, " ?> "), + Binary::QueGTE => write!(f, " ?>= "), + Binary::SLL => write!(f, " sll "), + Binary::SRL => write!(f, " srl "), + Binary::SLA => write!(f, " sla "), + Binary::SRA => write!(f, " sra "), + Binary::ROL => write!(f, " rol "), + Binary::ROR => write!(f, " ror "), + Binary::Plus => write!(f, " + "), + Binary::Minus => write!(f, " - "), + Binary::Concat => write!(f, " & "), + Binary::Times => write!(f, " * "), + Binary::Div => write!(f, " / "), + Binary::Mod => write!(f, " mod "), + Binary::Rem => write!(f, " rem "), + Binary::Pow => write!(f, " ** "), + } + } +} + +impl Display for AttributeName { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.name)?; + if let Some(ref signature) = self.signature { + write!(f, "{}", signature)?; + } + write!(f, "'{}", self.attr)?; + if let Some(ref expr) = self.expr { + write!(f, "({})", expr) + } else { + Ok(()) + } + } +} + +impl Display for ExternalObjectClass { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + ExternalObjectClass::Constant => write!(f, "constant"), + ExternalObjectClass::Signal => write!(f, "signal"), + ExternalObjectClass::Variable => write!(f, "variable"), + } + } +} + +impl Display for ExternalPath { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + ExternalPath::Package(ref path) => { + write!(f, "@{}", path) + } + ExternalPath::Absolute(ref path) => { + write!(f, ".{}", path) + } + ExternalPath::Relative(ref path, ref up_levels) => { + write!(f, "{}{}", "^.".repeat(*up_levels), path) + } + } + } +} + +impl Display for ExternalName { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "<< {} {} : {} >>", self.class, self.path, self.subtype) + } +} + +impl Display for Name { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Name::Designator(designator) => write!(f, "{}", designator), + Name::Selected(ref prefix, ref designator) => write!(f, "{}.{}", prefix, designator), + Name::SelectedAll(ref prefix) => write!(f, "{}.all", prefix), + Name::Indexed(ref prefix, ref exprs) => { + write!(f, "{}(", prefix)?; + let mut first = true; + for expr in exprs { + if first { + write!(f, "{}", expr)?; + } else { + write!(f, ", {}", expr)?; + } + first = false; + } + write!(f, ")") + } + Name::Slice(ref prefix, ref drange) => write!(f, "{}({})", prefix, drange), + Name::Attribute(ref attr) => write!(f, "{}", attr), + Name::FunctionCall(ref fcall) => write!(f, "{}", fcall), + Name::External(ref ename) => write!(f, "{}", ename), + } + } +} + +impl Display for SelectedName { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + SelectedName::Selected(ref prefix, ref des) => write!(f, "{}.{}", prefix, &des), + SelectedName::Designator(ref des) => write!(f, "{}", &des), + } + } +} + +impl Display for FunctionCall { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.name)?; + let mut first = true; + for param in &self.parameters { + if first { + write!(f, "({}", param)?; + } else { + write!(f, ", {}", param)?; + } + first = false; + } + if !first { + write!(f, ")") + } else { + Ok(()) + } + } +} + +impl Display for Choice { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Choice::Expression(ref expr) => write!(f, "{}", expr), + Choice::DiscreteRange(ref drange) => write!(f, "{}", drange), + Choice::Others => write!(f, "others"), + } + } +} + +impl Display for ElementAssociation { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + ElementAssociation::Positional(ref expr) => { + write!(f, "{}", expr) + } + ElementAssociation::Named(ref choices, ref expr) => { + let mut first = true; + for choice in choices { + if first { + write!(f, "{}", choice)?; + } else { + write!(f, " | {}", choice)?; + } + first = false; + } + write!(f, " => {}", expr) + } + } + } +} + +impl Display for ActualPart { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + ActualPart::Expression(ref expr) => write!(f, "{}", expr), + ActualPart::Open => write!(f, "open"), + } + } +} + +impl Display for AssociationElement { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + if let Some(ref formal) = self.formal { + write!(f, "{} => ", formal)?; + } + write!(f, "{}", self.actual) + } +} + +impl Display for AbstractLiteral { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + AbstractLiteral::Integer(val) => write!(f, "{}", val), + AbstractLiteral::Real(val) => write!(f, "{}", val), + } + } +} + +impl Display for BitString { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + if let Some(length) = self.length { + write!(f, "{}", length)?; + } + write!(f, "{}\"{}\"", self.base, self.value) + } +} + +impl Display for Literal { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Literal::String(ref val) => write!(f, "\"{}\"", val), + Literal::BitString(ref val) => write!(f, "{}", val), + Literal::Character(byte) => write!(f, "'{}'", *byte as char), + Literal::AbstractLiteral(ref val) => write!(f, "{}", val), + Literal::Physical(ref val, ref sym) => write!(f, "{} {}", val, sym), + Literal::Null => write!(f, "null"), + } + } +} + +impl Display for Allocator { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Allocator::Qualified(ref qexpr) => write!(f, "{}", qexpr), + Allocator::Subtype(ref subtype) => write!(f, "{}", subtype), + } + } +} + +impl Display for QualifiedExpression { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self.expr.item { + Expression::Aggregate(..) => write!(f, "{}'{}", self.name, self.expr), + _ => write!(f, "{}'({})", self.name, self.expr), + } + } +} + +fn binary_precedence(op: Binary) -> usize { + match op { + Binary::And => 2, + Binary::Or => 2, + Binary::Nand => 2, + Binary::Nor => 2, + Binary::Xor => 2, + Binary::Xnor => 2, + + Binary::EQ => 3, + Binary::NE => 3, + Binary::LT => 3, + Binary::LTE => 3, + Binary::GT => 3, + Binary::GTE => 3, + Binary::QueEQ => 3, + Binary::QueNE => 3, + Binary::QueLT => 3, + Binary::QueLTE => 3, + Binary::QueGT => 3, + Binary::QueGTE => 3, + + Binary::SLL => 4, + Binary::SRL => 4, + Binary::SLA => 4, + Binary::SRA => 4, + Binary::ROL => 4, + Binary::ROR => 4, + + Binary::Plus => 5, + Binary::Minus => 5, + Binary::Concat => 5, + + Binary::Times => 7, + Binary::Div => 7, + Binary::Mod => 7, + Binary::Rem => 7, + + Binary::Pow => 8, + } +} + +fn unary_precedence(op: Unary) -> usize { + match op { + Unary::And => 8, + Unary::Or => 8, + Unary::Nand => 8, + Unary::Nor => 8, + Unary::Xor => 8, + Unary::Xnor => 8, + + Unary::Abs => 8, + Unary::Not => 8, + Unary::Minus => 6, + Unary::Plus => 6, + Unary::QueQue => 1, + } +} + +impl Display for Expression { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Expression::Binary(ref op, ref lhs, ref rhs) => { + // Add parentheses as necessary to satisfy order of precedence. + let precedence = binary_precedence(*op); + match lhs.item { + Expression::Binary(op, ..) => { + if precedence <= binary_precedence(op) { + write!(f, "{}", lhs)?; + } else { + write!(f, "({})", lhs)?; + } + } + Expression::Unary(op, ..) => { + if precedence <= unary_precedence(op) { + write!(f, "{}", lhs)?; + } else { + write!(f, "({})", lhs)?; + } + } + _ => write!(f, "{}", lhs)?, + } + write!(f, "{}", op)?; + match rhs.item { + Expression::Binary(op, ..) => { + if precedence < binary_precedence(op) { + write!(f, "{}", rhs) + } else { + write!(f, "({})", rhs) + } + } + _ => write!(f, "{}", rhs), + } + } + Expression::Unary(ref op, ref expr) => { + // Add parentheses as necessary to satisfy order of precedence. + let precedence = unary_precedence(*op); + write!(f, "{}", op)?; + match expr.item { + // Binary operators having precedence over unary ones is + // confusing, so always add parentheses. + Expression::Binary(..) => { + write!(f, "({})", expr) + } + // Chained unary operators are always left to right, but + // chained operators with the same precedence are + // parenthesized for clarity. + Expression::Unary(op, ..) => { + if precedence != unary_precedence(op) { + write!(f, "{}", expr) + } else { + write!(f, "({})", expr) + } + } + _ => write!(f, "{}", expr), + } + } + Expression::Aggregate(ref assocs) => { + let mut first = true; + for assoc in assocs { + if first { + write!(f, "({}", assoc)?; + } else { + write!(f, ", {}", assoc)?; + } + first = false; + } + if !first { + write!(f, ")") + } else { + Ok(()) + } + } + Expression::Qualified(ref qexpr) => write!(f, "{}", qexpr), + Expression::Name(ref name) => write!(f, "{}", name), + Expression::Literal(ref literal) => write!(f, "{}", literal), + Expression::New(ref alloc) => write!(f, "new {}", alloc), + } + } +} + +impl Display for Direction { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Direction::Ascending => write!(f, "to"), + Direction::Descending => write!(f, "downto"), + } + } +} + +impl Display for DiscreteRange { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + DiscreteRange::Discrete(ref name, ref range) => { + write!(f, "{}", name)?; + match range { + Some(ref range) => write!(f, " range {}", range), + None => Ok(()), + } + } + DiscreteRange::Range(ref range) => { + write!(f, "{}", range) + } + } + } +} + +impl Display for RangeConstraint { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!( + f, + "{} {} {}", + self.left_expr, self.direction, self.right_expr, + ) + } +} + +impl Display for Range { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Range::Range(ref constraint) => write!(f, "{}", constraint), + Range::Attribute(ref attr) => write!(f, "{}", attr), + } + } +} + +impl Display for ElementConstraint { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}{}", self.ident, self.constraint) + } +} + +impl Display for SubtypeConstraint { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + SubtypeConstraint::Range(ref range) => { + write!(f, " range {}", range) + } + SubtypeConstraint::Array(ref dranges, ref constraint) => { + write!(f, "(")?; + let mut first = true; + for drange in dranges { + if first { + write!(f, "{}", drange)?; + } else { + write!(f, ", {}", drange)?; + } + first = false; + } + if first { + write!(f, "open")?; + } + match constraint { + Some(ref constraint) => write!(f, "){}", constraint), + None => write!(f, ")"), + } + } + SubtypeConstraint::Record(constraints) => { + write!(f, "(")?; + let mut first = true; + for constraint in constraints { + if first { + write!(f, "{}", constraint)?; + } else { + write!(f, ", {}", constraint)?; + } + first = false; + } + write!(f, ")") + } + } + } +} + +impl Display for RecordElementResolution { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{} {}", self.ident, self.resolution) + } +} + +impl Display for ResolutionIndication { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + ResolutionIndication::FunctionName(ref name) => { + write!(f, "{}", name) + } + ResolutionIndication::ArrayElement(ref name) => { + write!(f, "({})", name) + } + ResolutionIndication::Record(elem_resolutions) => { + let mut first = true; + for elem_resolution in elem_resolutions { + if first { + write!(f, "({}", elem_resolution)?; + } else { + write!(f, ", {}", elem_resolution)?; + } + first = false; + } + if !first { + write!(f, ")") + } else { + Ok(()) + } + } + ResolutionIndication::Unresolved => Ok(()), + } + } +} + +impl Display for SubtypeIndication { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self.resolution { + ResolutionIndication::Unresolved => (), + _ => write!(f, "{} ", self.resolution)?, + } + write!(f, "{}", self.type_mark)?; + match self.constraint { + Some(ref constraint) => write!(f, "{}", constraint), + None => Ok(()), + } + } +} + +impl Display for ArrayIndex { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + ArrayIndex::IndexSubtypeDefintion(ref type_mark) => { + write!(f, "{} range <>", type_mark) + } + ArrayIndex::Discrete(ref range) => { + write!(f, "{}", range) + } + } + } +} + +impl Display for ElementDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{} : {}", self.ident, self.subtype) + } +} + impl Display for Designator { fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self { @@ -25,17 +610,1530 @@ impl Display for WithRef { } } -impl Display for SelectedName { +impl Display for AliasDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "alias {}", self.designator)?; + if let Some(ref subtype_indication) = self.subtype_indication { + write!(f, " : {}", subtype_indication)?; + } + write!(f, " is {}", self.name)?; + match self.signature { + Some(ref signature) => write!(f, "{};", signature), + None => write!(f, ";"), + } + } +} + +impl Display for EnumerationLiteral { fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self { - SelectedName::Selected(ref prefix, ref des) => write!(f, "{}.{}", prefix, &des), - SelectedName::Designator(ref des) => write!(f, "{}", &des), + EnumerationLiteral::Identifier(ref sym) => write!(f, "{}", sym), + EnumerationLiteral::Character(byte) => write!(f, "'{}'", *byte as char), } } } -impl Display for WithPos { +impl Display for TypeDefinition { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", &self.item) + match self { + TypeDefinition::Enumeration(ref enum_literals) => { + write!(f, " is (")?; + let mut first = true; + for literal in enum_literals { + if first { + write!(f, "{}", literal)?; + } else { + write!(f, ", {}", literal)?; + } + first = false; + } + write!(f, ");") + } + TypeDefinition::Integer(ref constraint) => { + write!(f, " is range {};", constraint) + } + TypeDefinition::Physical(ref physical) => { + writeln!( + f, + " is range {} units\n {};", + physical.range, physical.primary_unit + )?; + for (ident, literal) in &physical.secondary_units { + writeln!(f, " {} = {};", ident, literal)?; + } + write!(f, "end units;") + } + TypeDefinition::Array(ref indexes, ref subtype_indication) => { + write!(f, " is array (")?; + let mut first = true; + for index in indexes { + if first { + write!(f, "{}", index)?; + } else { + write!(f, ", {}", index)?; + } + first = false; + } + write!(f, ") of {};", subtype_indication) + } + TypeDefinition::Record(ref elements) => { + writeln!(f, " is record")?; + for element in elements { + writeln!(f, " {};", element)?; + } + write!(f, "end record;") + } + TypeDefinition::Access(ref subtype_indication) => { + write!(f, " is access {};", subtype_indication) + } + TypeDefinition::Incomplete(..) => { + write!(f, ";") + } + TypeDefinition::File(ref type_mark) => { + write!(f, " is file of {};", type_mark) + } + TypeDefinition::Protected(..) => { + // Not used: items + write!(f, " is protected") + } + TypeDefinition::ProtectedBody(..) => { + // Not used: type_reference, decl + write!(f, " is protected body") + } + TypeDefinition::Subtype(ref subtype_indication) => { + write!(f, " is {};", subtype_indication) + } + } + } +} + +impl Display for TypeDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self.def { + TypeDefinition::Subtype(..) => write!(f, "subtype")?, + _ => write!(f, "type")?, + } + write!(f, " {}{}", self.ident, self.def) + } +} + +impl Display for ObjectClass { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + ObjectClass::Signal => write!(f, "signal"), + ObjectClass::Constant => write!(f, "constant"), + ObjectClass::Variable => write!(f, "variable"), + ObjectClass::SharedVariable => write!(f, "shared variable"), + } + } +} + +impl Display for ObjectDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!( + f, + "{} {} : {}", + self.class, self.ident, self.subtype_indication, + )?; + match self.expression { + Some(ref expr) => write!(f, " := {};", expr), + None => write!(f, ";"), + } + } +} + +impl Display for FileDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "file {} : {}", self.ident, self.subtype_indication)?; + if let Some(ref expr) = self.open_info { + write!(f, " open {}", expr)?; + } + match self.file_name { + Some(ref expr) => write!(f, " is {};", expr), + None => write!(f, ";"), + } + } +} + +impl Display for SubprogramDesignator { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + SubprogramDesignator::Identifier(ref sym) => write!(f, "{}", sym), + SubprogramDesignator::OperatorSymbol(ref latin1) => write!(f, "\"{}\"", latin1), + } + } +} + +impl Display for ProcedureSpecification { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "procedure {}", self.designator)?; + let mut first = true; + for param in &self.parameter_list { + if first { + write!(f, "(\n {}", param)?; + } else { + write!(f, ";\n {}", param)?; + } + first = false; + } + if !first { + write!(f, "\n)") + } else { + Ok(()) + } + } +} + +impl Display for FunctionSpecification { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + if !self.pure { + write!(f, "impure ")?; + } + write!(f, "function {}", self.designator)?; + let mut first = true; + for param in &self.parameter_list { + if first { + write!(f, "(\n {}", param)?; + } else { + write!(f, ";\n {}", param)?; + } + first = false; + } + if !first { + write!(f, "\n)")?; + } + write!(f, " return {}", self.return_type) + } +} + +impl Display for Signature { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Signature::Function(ref args, ref ret) => { + write!(f, "[")?; + let mut first = true; + for arg in args { + if first { + write!(f, "{}", arg)?; + } else { + write!(f, ", {}", arg)?; + } + first = false; + } + if first { + write!(f, "return {}]", ret) + } else { + write!(f, " return {}]", ret) + } + } + Signature::Procedure(ref args) => { + write!(f, "[")?; + let mut first = true; + for arg in args { + if first { + write!(f, "{}", arg)?; + } else { + write!(f, ", {}", arg)?; + } + first = false; + } + write!(f, "]") + } + } + } +} + +impl Display for SubprogramDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + SubprogramDeclaration::Procedure(ref procedure) => write!(f, "{}", procedure), + SubprogramDeclaration::Function(ref function) => write!(f, "{}", function), + } + } +} + +impl Display for InterfaceFileDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "file {} : {}", self.ident, self.subtype_indication) + } +} + +impl Display for InterfaceObjectDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self.list_type { + InterfaceListType::Port => { + write!( + f, + "{} : {} {}", + self.ident, self.mode, self.subtype_indication + )?; + } + InterfaceListType::Generic => { + write!(f, "{} : {}", self.ident, self.subtype_indication)?; + } + InterfaceListType::Parameter => { + write!( + f, + "{} {} : {} {}", + self.class, self.ident, self.mode, self.subtype_indication, + )?; + } + } + match self.expression { + Some(ref expr) => write!(f, " := {}", expr), + None => Ok(()), + } + } +} + +impl Display for SubprogramDefault { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + SubprogramDefault::Name(ref name) => write!(f, "{}", name), + SubprogramDefault::Box => write!(f, "<>"), + } + } +} + +impl Display for InterfacePackageDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!( + f, + "package {} is new {}\n generic map (", + self.ident, self.package_name + )?; + match &self.generic_map { + InterfacePackageGenericMapAspect::Map(assoc_list) => { + let mut first = true; + for assoc in assoc_list { + if first { + write!(f, "\n {}", assoc)?; + } else { + write!(f, ",\n {}", assoc)?; + } + first = false; + } + write!(f, "\n )") + } + InterfacePackageGenericMapAspect::Box => write!(f, "<>)"), + InterfacePackageGenericMapAspect::Default => write!(f, "default)"), + } + } +} + +impl Display for InterfaceDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + InterfaceDeclaration::Object(ref decl) => write!(f, "{}", decl), + InterfaceDeclaration::File(ref decl) => write!(f, "{}", decl), + InterfaceDeclaration::Type(ref ident) => write!(f, "type {}", ident), + InterfaceDeclaration::Subprogram(ref decl, ref default) => { + write!(f, "{}", decl)?; + match default { + Some(ref default) => write!(f, " is {}", default), + None => Ok(()), + } + } + InterfaceDeclaration::Package(ref decl) => write!(f, "{}", decl), + } + } +} + +impl Display for Mode { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Mode::In => write!(f, "in"), + Mode::Out => write!(f, "out"), + Mode::InOut => write!(f, "inout"), + Mode::Buffer => write!(f, "buffer"), + Mode::Linkage => write!(f, "linkage"), + } + } +} + +impl Display for ComponentDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "component {}", self.ident)?; + + let mut first = true; + for generic in &self.generic_list { + if first { + write!(f, "\n generic (\n {}", generic)?; + } else { + write!(f, ";\n {}", generic)?; + } + first = false; + } + if !first { + write!(f, "\n );")?; + } + + let mut first = true; + for port in &self.port_list { + if first { + write!(f, "\n port (\n {}", port)?; + } else { + write!(f, ";\n {}", port)?; + } + first = false; + } + if !first { + write!(f, "\n );")?; + } + + write!(f, "\nend component;") + } +} + +impl Display for ForGenerateStatement { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + // Not used: body + write!( + f, + "for {} in {} generate", + self.index_name, self.discrete_range, + ) + } +} + +impl Display for ContextDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + // Not used: items + write!(f, "context {}", self.ident) + } +} + +impl Display for PackageInstantiation { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + // Not used: context_clause + write!(f, "package {} is new {}", self.ident, self.package_name)?; + if let Some(assoc_list) = &self.generic_map { + let mut first = true; + for assoc in assoc_list { + if first { + write!(f, "\n generic map (\n {}", assoc)?; + } else { + write!(f, ",\n {}", assoc)?; + } + first = false; + } + if !first { + write!(f, "\n )")?; + } + } + write!(f, ";") + } +} + +impl Display for ConfigurationDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + // Not used: context_clause, decl, block_config, vunit_bind_inds + write!(f, "configuration {} of {}", self.ident, self.entity_name) + } +} + +impl Display for EntityDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + // Not used: context_clause, decl, statements + write!(f, "entity {} is", self.ident)?; + + if let Some(generic_clause) = &self.generic_clause { + let mut first = true; + for generic in generic_clause { + if first { + write!(f, "\n generic (\n {}", generic)?; + } else { + write!(f, ";\n {}", generic)?; + } + first = false; + } + if !first { + write!(f, "\n );")?; + } + } + + if let Some(port_clause) = &self.port_clause { + let mut first = true; + for port in port_clause { + if first { + write!(f, "\n port (\n {}", port)?; + } else { + write!(f, ";\n {}", port)?; + } + first = false; + } + if !first { + write!(f, "\n );")?; + } + } + + write!(f, "\nend entity;") + } +} + +impl Display for PackageDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + // Not used: context_clause, decl + if let Some(generic_clause) = &self.generic_clause { + write!(f, "package {} is", self.ident)?; + + let mut first = true; + for generic in generic_clause { + if first { + write!(f, "\n generic (\n {}", generic)?; + } else { + write!(f, ";\n {}", generic)?; + } + first = false; + } + if !first { + write!(f, "\n );") + } else { + Ok(()) + } + } else { + write!(f, "package {}", self.ident) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::syntax::test::Code; + use assert_matches::assert_matches; + + pub fn assert_format_eq(code: &str, res: &str, code_fun: F) + where + F: FnOnce(&Code) -> R, + { + assert_eq!(format!("{}", code_fun(&Code::new(code))), res); + } + + pub fn assert_format(code: &str, code_fun: F) + where + F: FnOnce(&Code) -> R, + { + assert_format_eq(code, code, code_fun); + } + + #[test] + fn test_selected_name_single() { + assert_format("foo", Code::selected_name); + } + + #[test] + fn test_selected_name_multiple() { + assert_format("foo.bar.baz", Code::selected_name); + } + + #[test] + fn test_name_operator_symbol() { + assert_format("\"+\"", Code::name); + } + + #[test] + fn test_name_character() { + assert_format("'a'", Code::name); + } + + #[test] + fn test_name_selected() { + assert_format("foo.bar.baz", Code::name); + } + + #[test] + fn test_name_selected_all() { + assert_format("foo.all", Code::name); + } + + #[test] + fn test_name_indexed_single() { + assert_format("foo(0)", Code::name); + } + + #[test] + fn test_name_indexed_multi() { + assert_format("foo(0, 1)", Code::name); + } + + #[test] + fn test_name_slice() { + assert_format("foo(0 to 1)", Code::name); + } + + #[test] + fn test_name_attribute() { + assert_format("prefix'foo", Code::name); + } + + #[test] + fn test_name_attribute_expression() { + assert_format("prefix'foo(expr + 1)", Code::name); + } + + #[test] + fn test_name_attribute_signature() { + assert_format("prefix[return natural]'foo", Code::name); + } + + #[test] + fn test_name_attribute_signature_expression() { + assert_format("prefix[return natural]'foo(expr + 1)", Code::name); + } + + #[test] + fn test_name_function_call_no_args() { + assert_format("foo", |code| { + Name::FunctionCall(Box::new(code.function_call())) + }); + } + + #[test] + fn test_name_function_call_no_formal() { + assert_format("foo(0)", Code::name); + } + + #[test] + fn test_name_function_call_chained() { + assert_format("prefix(0, 1)(3)", Code::name); + } + + #[test] + fn test_name_function_call_formal() { + assert_format("foo(arg => 0)", Code::name); + } + + #[test] + fn test_name_function_call_actual_part_open() { + assert_format("foo(open, arg => open)", Code::name); + } + + #[test] + fn test_name_external_implicit_relative() { + assert_format("<< signal dut.foo : std_logic >>", Code::name); + } + + #[test] + fn test_name_external_explicit_relative() { + assert_format("<< signal ^.dut.gen(0) : std_logic >>", Code::name); + } + + #[test] + fn test_name_external_explicit_relative_multiple_levels() { + assert_format("<< signal ^.^.^.dut.gen(0) : std_logic >>", Code::name); + } + + #[test] + fn test_name_external_absolute() { + assert_format("<< signal .dut.gen(0) : std_logic >>", Code::name); + } + + #[test] + fn test_name_external_package() { + assert_format("<< signal @lib.pkg : std_logic >>", Code::name); + } + + #[test] + fn test_name_external_object_classes() { + assert_format("<< constant dut.foo : std_logic >>", Code::name); + assert_format("<< signal dut.foo : std_logic >>", Code::name); + assert_format("<< variable dut.foo : std_logic >>", Code::name); + } + + #[test] + fn test_expression_binary() { + assert_format("1 ** 2", Code::expr); + assert_format("1 * 2", Code::expr); + assert_format("1 / 2", Code::expr); + assert_format("1 mod 2", Code::expr); + assert_format("1 rem 2", Code::expr); + assert_format("1 + 2", Code::expr); + assert_format("1 - 2", Code::expr); + assert_format("1 & 2", Code::expr); + assert_format("1 sll 2", Code::expr); + assert_format("1 srl 2", Code::expr); + assert_format("1 sla 2", Code::expr); + assert_format("1 sra 2", Code::expr); + assert_format("1 rol 2", Code::expr); + assert_format("1 ror 2", Code::expr); + assert_format("1 = 2", Code::expr); + assert_format("1 /= 2", Code::expr); + assert_format("1 < 2", Code::expr); + assert_format("1 <= 2", Code::expr); + assert_format("1 > 2", Code::expr); + assert_format("1 >= 2", Code::expr); + assert_format("1 ?= 2", Code::expr); + assert_format("1 ?/= 2", Code::expr); + assert_format("1 ?< 2", Code::expr); + assert_format("1 ?<= 2", Code::expr); + assert_format("1 ?> 2", Code::expr); + assert_format("1 ?>= 2", Code::expr); + assert_format("1 and 2", Code::expr); + assert_format("1 or 2", Code::expr); + assert_format("1 nand 2", Code::expr); + assert_format("1 nor 2", Code::expr); + assert_format("1 xor 2", Code::expr); + assert_format("1 xnor 2", Code::expr); + } + + #[test] + fn test_expression_unary() { + assert_format("?? 1", Code::expr); + assert_format("+1", Code::expr); + assert_format("-1", Code::expr); + assert_format("not 1", Code::expr); + assert_format("abs 1", Code::expr); + assert_format("and 1", Code::expr); + assert_format("or 1", Code::expr); + assert_format("nand 1", Code::expr); + assert_format("nor 1", Code::expr); + assert_format("xor 1", Code::expr); + assert_format("xnor 1", Code::expr); + } + + #[test] + fn test_expression_precedence() { + assert_format("1 * 2 + 3 * 4", Code::expr); + assert_format("(1 + 2) * (3 + 4)", Code::expr); + assert_format("1 + 2 + (3 + 4)", Code::expr); + assert_format("-1 + -2", Code::expr); + assert_format("-(1 + 2)", Code::expr); + // Multiplication has precedence over negation + assert_format("(-1) * -2", Code::expr); + assert_format("-(1 * -2)", Code::expr); + assert_format("-(-1)", Code::expr); + assert_format("-(+1)", Code::expr); + assert_format("-not 1", Code::expr); + assert_format("not -1", Code::expr); + assert_format("not (not 1)", Code::expr); + assert_format("1 - -1", Code::expr); + } + + #[test] + fn test_expression_aggregate_positional() { + assert_format("(1, 2)", Code::expr); + } + + #[test] + fn test_expression_aggregate_named_expression() { + assert_format("(1 => 2)", Code::expr); + } + + #[test] + fn test_expression_aggregate_named_many_choices() { + assert_format("(1 | 2 => 3)", Code::expr); + } + + #[test] + fn test_expression_aggregate_many_named_others() { + assert_format("(1 | 2 => 3, others => 4)", Code::expr); + } + + #[test] + fn test_expression_aggregate_named_range() { + assert_format("(0 to 1 => 2)", Code::expr); + assert_format("(1 downto 0 => 2)", Code::expr); + } + + #[test] + fn test_expression_qualified() { + assert_format("foo'(1 + 2)", Code::expr); + } + + #[test] + fn test_expression_name() { + assert_format("foo.bar.baz", Code::expr); + } + + #[test] + fn test_expression_literal_string() { + assert_format("\"string\"", Code::expr); + } + + #[test] + fn test_expression_literal_bit_string() { + assert_format("b\"0110\"", Code::expr); + assert_format("o\"1377\"", Code::expr); + assert_format("x\"Aa5F\"", Code::expr); + assert_format("ub\"0110\"", Code::expr); + assert_format("uo\"1377\"", Code::expr); + assert_format("ux\"Aa5F\"", Code::expr); + assert_format("sb\"0110\"", Code::expr); + assert_format("so\"1377\"", Code::expr); + assert_format("sx\"Aa5F\"", Code::expr); + assert_format("d\"1234\"", Code::expr); + } + + #[test] + fn test_expression_literal_bit_string_with_length() { + assert_format("3b\"0110\"", Code::expr); + assert_format("10o\"1377\"", Code::expr); + assert_format("15x\"5FaA\"", Code::expr); + assert_format("3ub\"0110\"", Code::expr); + assert_format("10uo\"1377\"", Code::expr); + assert_format("15ux\"5FaA\"", Code::expr); + assert_format("3sb\"0110\"", Code::expr); + assert_format("10so\"1377\"", Code::expr); + assert_format("15sx\"5FaA\"", Code::expr); + assert_format("12d\"1234\"", Code::expr); + } + + #[test] + fn test_expression_literal_character() { + assert_format("'a'", Code::expr); + } + + #[test] + fn test_expression_literal_integer() { + assert_format("123", Code::expr); + } + + #[test] + fn test_expression_literal_real() { + assert_format("12.3", Code::expr); + } + + #[test] + fn test_expression_literal_physical_integer() { + assert_format("1 ns", Code::expr); + } + + #[test] + fn test_expression_literal_physical_real() { + assert_format("1.1 ns", Code::expr); + } + + #[test] + fn parses_null_literal() { + assert_format("null", Code::expr); + } + + #[test] + fn test_expression_new_allocator_qualified() { + assert_format("new integer_vector'(0, 1)", Code::expr); + } + + #[test] + fn test_expression_new_allocator_subtype() { + assert_format("new integer_vector", Code::expr); + } + + #[test] + fn test_expression_new_allocator_subtype_constraint() { + assert_format("new integer_vector(0 to 1)", Code::expr); + } + + #[test] + fn test_expression_new_allocator_subtype_constraint_range_attribute() { + assert_format("new integer_vector(foo'range)", Code::expr); + } + + #[test] + fn test_discrete_range() { + assert_format("foo.bar", Code::discrete_range); + } + + #[test] + fn test_discrete_range_range() { + assert_format("foo.bar range 1 to 4", Code::discrete_range); + } + + #[test] + fn test_discrete_range_range_attribute() { + assert_format("foo.bar'range", Code::discrete_range); + } + + #[test] + fn test_subtype_indication_without_constraint() { + assert_format("std_logic", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_resolution_function() { + assert_format("resolve std_logic", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_array_element_resolution_function() { + assert_format("(resolve) integer_vector", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_record_element_resolution_function() { + assert_format("(elem resolve) rec_t", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_record_element_resolution_function_many() { + assert_format( + "(elem1 (resolve1), elem2 resolve2, elem3 (sub_elem sub_resolve)) rec_t", + Code::subtype_indication, + ); + } + + #[test] + fn test_subtype_indication_with_resolution_function_selected_name() { + assert_format("lib.foo.resolve std_logic", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_range() { + assert_format("integer range 0 to 2 - 1", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_range_attribute() { + assert_format("integer range lib.foo.bar'range", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_array_constraint_range() { + assert_format("integer_vector(2 - 1 downto 0)", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_array_constraint_discrete() { + assert_format("integer_vector(lib.foo.bar)", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_array_constraint_attribute() { + assert_format( + "integer_vector(lib.pkg.bar'range)", + Code::subtype_indication, + ); + } + + #[test] + fn test_subtype_indication_with_array_constraint_open() { + assert_format("integer_vector(open)", Code::subtype_indication); + } + + #[test] + fn test_subtype_indication_with_multi_dim_array_constraints() { + assert_format( + "integer_vector(2 - 1 downto 0, 11 to 14)", + Code::subtype_indication, + ); + } + + #[test] + fn test_subtype_indication_with_array_element_constraint() { + assert_format( + "integer_vector(2 - 1 downto 0, 11 to 14)(foo to bar)", + Code::subtype_indication, + ); + } + + #[test] + fn test_subtype_indication_with_record_constraint() { + assert_format( + "axi_m2s_t(tdata(2 - 1 downto 0), tuser(3 to 5))", + Code::subtype_indication, + ); + } + + #[test] + fn test_type_declaration_integer() { + assert_format("type foo is range 0 to 1;", Code::type_decl); + } + + #[test] + fn test_type_declaration_enumeration() { + assert_format("type foo is (alpha, beta);", Code::type_decl); + } + + #[test] + fn test_type_declaration_enumeration_character() { + assert_format("type foo is ('a', 'b');", Code::type_decl); + } + + #[test] + fn test_type_declaration_enumeration_mixed() { + assert_format("type foo is (ident, 'b');", Code::type_decl); + } + + #[test] + fn test_type_declaration_array_with_index_subtype() { + assert_format( + "type foo is array (natural range <>) of boolean;", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_array_with_discrete_subtype() { + assert_format("type foo is array (natural) of boolean;", Code::type_decl); + } + + #[test] + fn test_type_declaration_array_with_selected_name() { + assert_format( + "type foo is array (lib.pkg.foo) of boolean;", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_array_with_range_attribute() { + assert_format( + "type foo is array (arr_t'range) of boolean;", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_array_with_constraint() { + assert_format( + "type foo is array (2 - 1 downto 0) of boolean;", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_array_mixed() { + assert_format( + "type foo is array (2 - 1 downto 0, integer range <>) of boolean;", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_record() { + assert_format( + "type foo is record + element : boolean; +end record;", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_record_many() { + assert_format( + "type foo is record + element : boolean; + field : boolean; + other_element : std_logic_vector(0 to 1); +end record;", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_subtype() { + assert_format( + "subtype vec_t is integer_vector(2 - 1 downto 0);", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_access() { + assert_format( + "type ptr_t is access integer_vector(2 - 1 downto 0);", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_incomplete() { + assert_format("type incomplete;", Code::type_decl); + } + + #[test] + fn test_type_declaration_file() { + assert_format("type foo is file of character;", Code::type_decl); + } + + #[test] + fn test_type_declaration_protected() { + assert_format_eq( + "type foo is protected +end protected;", + "type foo is protected", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_protected_with_subprograms() { + assert_format_eq( + "type foo is protected + procedure proc; + function fun return ret; +end protected;", + "type foo is protected", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_protected_body() { + assert_format_eq( + "type foo is protected body + variable foo : natural; + procedure proc is + begin + end; +end protected body;", + "type foo is protected body", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_physical() { + assert_format( + "type phys is range 0 to 15 units + primary_unit; +end units;", + Code::type_decl, + ); + } + + #[test] + fn test_type_declaration_physical_secondary_units() { + assert_format( + "type phys is range 0 to 15 units + primary_unit; + secondary_unit = 5 primary_unit; +end units;", + Code::type_decl, + ); + } + + #[test] + fn test_object_declaration_constant() { + assert_format("constant foo : natural;", Code::object_decl); + } + + #[test] + fn test_object_declaration_signal() { + assert_format("signal foo : natural;", Code::object_decl); + } + + #[test] + fn test_object_declaration_variable() { + assert_format("variable foo : natural;", Code::object_decl); + } + + #[test] + fn test_object_declaration_shared_variable() { + assert_format("shared variable foo : natural;", Code::object_decl); + } + + #[test] + fn test_object_declaration_optional_expression() { + assert_format("constant foo : natural := 0;", Code::object_decl); + } + + #[test] + fn test_file_declaration() { + assert_format("file foo : text;", Code::file_decl); + } + + #[test] + fn test_file_declaration_with_file_name() { + assert_format("file foo : text is \"file_name\";", Code::file_decl); + } + + #[test] + fn test_file_declaration_with_open_information() { + assert_format( + "file foo : text open write_mode is \"file_name\";", + Code::file_decl, + ); + } + + #[test] + fn test_alias_declaration() { + assert_format("alias foo is name;", Code::alias_decl); + } + + #[test] + fn test_alias_declaration_with_subtype_indication() { + assert_format("alias foo : vector(0 to 1) is name;", Code::alias_decl); + } + + #[test] + fn test_alias_declaration_with_signature() { + assert_format("alias foo is name[return natural];", Code::alias_decl); + } + + #[test] + fn test_alias_declaration_with_operator_symbol() { + assert_format("alias \"and\" is name;", Code::alias_decl); + } + + #[test] + fn test_alias_declaration_with_character() { + assert_format("alias 'c' is 'b';", Code::alias_decl); + } + + #[test] + pub fn test_procedure_specification() { + assert_format("procedure foo", Code::subprogram_decl); + } + + #[test] + pub fn test_function_specification() { + assert_format("function foo return lib.foo.natural", Code::subprogram_decl); + } + + #[test] + pub fn test_function_specification_operator() { + assert_format( + "function \"+\" return lib.foo.natural", + Code::subprogram_decl, + ); + } + + #[test] + pub fn test_function_specification_impure() { + assert_format( + "impure function foo return lib.foo.natural", + Code::subprogram_decl, + ); + } + + #[test] + pub fn test_procedure_specification_with_parameters() { + assert_format( + "procedure foo( + constant foo : in natural +)", + Code::subprogram_decl, + ); + } + + #[test] + pub fn test_function_specification_with_parameters() { + assert_format( + "function foo( + constant foo : in natural +) return lib.foo.natural", + Code::subprogram_decl, + ); + } + + #[test] + pub fn test_interface_declaration_object() { + assert_format("signal foo : in std_logic", Code::parameter); + assert_format("signal foo : out std_logic", Code::parameter); + assert_format("signal foo : inout std_logic", Code::parameter); + assert_format("signal foo : buffer std_logic", Code::parameter); + assert_format("signal foo : linkage std_logic", Code::parameter); + + assert_format("constant foo : in std_logic", Code::parameter); + + assert_format("variable foo : in std_logic", Code::parameter); + assert_format("variable foo : out std_logic", Code::parameter); + assert_format("variable foo : inout std_logic", Code::parameter); + assert_format("variable foo : buffer std_logic", Code::parameter); + assert_format("variable foo : linkage std_logic", Code::parameter); + } + + #[test] + fn test_interface_declaration_object_with_expression() { + assert_format("constant foo : in natural := bar(0)", Code::parameter); + } + + #[test] + fn test_interface_declaration_object_generic() { + assert_format("foo : natural := bar(0)", Code::generic); + } + + #[test] + fn test_interface_declaration_object_port() { + assert_format("foo : in natural := bar(0)", Code::port); + } + + #[test] + fn test_interface_declaration_file() { + assert_format("file foo : text", Code::parameter); + } + + #[test] + fn test_interface_declaration_type() { + assert_format("type name", Code::parameter); + } + + #[test] + fn test_interface_declaration_subprogram() { + assert_format("function foo return bar", Code::parameter); + assert_format("procedure foo", Code::parameter); + assert_format("impure function foo return bar", Code::parameter); + } + + #[test] + fn test_interface_declaration_subprogram_default() { + assert_format("function foo return bar is lib.name", Code::parameter); + assert_format("function foo return bar is <>", Code::parameter); + } + + #[test] + fn test_interface_declaration_package_map() { + assert_format( + "package foo is new lib.pkg + generic map ( + foo => bar + )", + Code::parameter, + ); + } + + #[test] + fn test_interface_declaration_package_box() { + assert_format( + "package foo is new lib.pkg + generic map (<>)", + Code::parameter, + ); + } + + #[test] + fn test_interface_declaration_package_default() { + assert_format( + "package foo is new lib.pkg + generic map (default)", + Code::parameter, + ); + } + + #[test] + pub fn test_signature_function_only_return() { + assert_format("[return bar.type_mark]", Code::signature); + } + + #[test] + pub fn test_signature_function_one_argument() { + assert_format("[foo.type_mark return bar.type_mark]", Code::signature); + } + + #[test] + pub fn test_signature_function_many_arguments() { + assert_format( + "[foo.type_mark, foo2.type_mark return bar.type_mark]", + Code::signature, + ); + } + + #[test] + pub fn test_signature_procedure() { + assert_format("[foo.type_mark]", Code::signature); + } + + #[test] + fn test_component_declaration() { + assert_format( + "component foo +end component;", + Code::component_decl, + ); + } + + #[test] + fn test_component_declaration_with_generic() { + assert_format( + "component foo + generic ( + foo : natural + ); +end component;", + Code::component_decl, + ); + } + + #[test] + fn test_component_declaration_with_port() { + assert_format( + "component foo + port ( + foo : inout natural + ); +end component;", + Code::component_decl, + ); + } + + #[test] + fn test_component_declaration_with_multiple() { + assert_format( + "component foo + generic ( + foo : natural; + bar : natural + ); + port ( + baz : in natural; + qux : out std_logic + ); +end component;", + Code::component_decl, + ); + } + + #[test] + fn test_entity_declaration() { + assert_format( + "entity foo is +end entity;", + Code::entity_decl, + ); + } + + #[test] + fn test_entity_declaration_with_generic() { + assert_format( + "entity foo is + generic ( + foo : natural + ); +end entity;", + Code::entity_decl, + ); + } + + #[test] + fn test_entity_declaration_with_port() { + assert_format( + "entity foo is + port ( + foo : inout natural + ); +end entity;", + Code::entity_decl, + ); + } + + #[test] + fn test_entity_declaration_with_multiple() { + assert_format( + "entity foo is + generic ( + foo : natural; + bar : natural + ); + port ( + baz : in natural; + qux : out std_logic + ); +end entity;", + Code::entity_decl, + ); + } + + #[test] + fn test_for_generate_statement() { + assert_format_eq( + "for idx in 0 to 1 generate +end generate;", + "for idx in 0 to 1 generate", + |code| { + assert_matches!( + code.concurrent_statement().statement, + ConcurrentStatement::ForGenerate(gen) => gen + ) + }, + ); + } + + #[test] + fn test_context_declaration() { + assert_format_eq( + "context ident is +end context;", + "context ident", + |code| { + assert_matches!( + code.design_file().design_units.remove(0), + AnyDesignUnit::Primary(AnyPrimaryUnit::Context(context)) => context + ) + }, + ); + } + + #[test] + fn test_package_instantiation() { + assert_format("package ident is new lib.foo.bar;", |code| { + assert_matches!( + code.design_file().design_units.remove(0), + AnyDesignUnit::Primary(AnyPrimaryUnit::PackageInstance(instance)) => instance + ) + }); + } + + #[test] + fn test_package_instantiation_generic_map() { + assert_format( + "package ident is new lib.foo.bar + generic map ( + foo => bar, + baz => qux + );", + |code| { + assert_matches!( + code.design_file().design_units.remove(0), + AnyDesignUnit::Primary(AnyPrimaryUnit::PackageInstance(instance)) => instance + ) + }, + ); + } + + #[test] + fn test_configuration_declaration() { + assert_format_eq( + "configuration cfg of entity_name is + for rtl(0) + end for; +end;", + "configuration cfg of entity_name", + |code| { + assert_matches!( + code.design_file().design_units.remove(0), + AnyDesignUnit::Primary(AnyPrimaryUnit::Configuration(unit)) => unit + ) + }, + ); + } + + #[test] + fn test_package_declaration() { + assert_format_eq( + "package pkg_name is +end package;", + "package pkg_name", + |code| { + assert_matches!( + code.design_file().design_units.remove(0), + AnyDesignUnit::Primary(AnyPrimaryUnit::Package(unit)) => unit + ) + }, + ); + } + + #[test] + fn test_package_declaration_with_generic() { + assert_format_eq( + "package pkg_name is + generic ( + type foo; + type bar + ); +end package;", + "package pkg_name is + generic ( + type foo; + type bar + );", + |code| { + assert_matches!( + code.design_file().design_units.remove(0), + AnyDesignUnit::Primary(AnyPrimaryUnit::Package(unit)) => unit + ) + }, + ); } } diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index ff87d4f08..8145d883d 100644 --- a/vhdl_lang/src/ast/search.rs +++ b/vhdl_lang/src/ast/search.rs @@ -38,6 +38,31 @@ impl SearchState { } } +#[derive(PartialEq, Debug, Clone)] +pub enum FoundDeclaration<'a> { + Object(&'a ObjectDeclaration), + InterfaceObject(&'a InterfaceObjectDeclaration), + File(&'a FileDeclaration), + Type(&'a TypeDeclaration), + Component(&'a ComponentDeclaration), + //Attribute(&'a AttributeDeclaration), + Alias(&'a AliasDeclaration), + Function(&'a FunctionSpecification), + Procedure(&'a ProcedureSpecification), + Library(&'a Ident), + Package(&'a PackageDeclaration), + PackageInstance(&'a PackageInstantiation), + Configuration(&'a ConfigurationDeclaration), + Entity(&'a EntityDeclaration), + Context(&'a ContextDeclaration), + ForIndex(&'a Ident, &'a DiscreteRange), + ForGenerateIndex(&'a Option, &'a ForGenerateStatement), + // Not used + //ConcurrentStatement(&'a LabeledConcurrentStatement), + //GenerateBody(&'a GenerateBody), + //SequentialStatement(&'a LabeledSequentialStatement), +} + pub trait Searcher { /// Search an position that has a reference to a declaration fn search_pos_with_ref(&mut self, _pos: &SrcPos, _ref: &Reference) -> SearchState { @@ -63,6 +88,11 @@ pub trait Searcher { NotFinished } + /// Search a declaration of a named entity + fn search_decl(&mut self, pos: &SrcPos, _decl: FoundDeclaration) -> SearchState { + self.search_decl_pos(pos) + } + fn search_with_pos(&mut self, _pos: &SrcPos) -> SearchState { NotFinished } @@ -285,7 +315,9 @@ impl Search for LabeledSequentialStatement { } = loop_stmt; match iteration_scheme { Some(IterationScheme::For(ref index, ref drange)) => { - return_if_found!(searcher.search_decl_pos(index.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(index.pos(), FoundDeclaration::ForIndex(&index, &drange)) + .or_not_found()); return_if_found!(drange.search(searcher)); return_if_found!(statements.search(searcher)); } @@ -400,7 +432,12 @@ impl Search for LabeledConcurrentStatement { discrete_range, body, } = gen; - return_if_found!(searcher.search_decl_pos(index_name.pos()).or_not_found()); + return_if_found!(searcher + .search_decl( + index_name.pos(), + FoundDeclaration::ForGenerateIndex(&self.label, &gen) + ) + .or_not_found()); return_if_found!(discrete_range.search(searcher)); body.search(searcher) } @@ -623,7 +660,9 @@ impl Search for TypeDeclaration { return_if_found!(body.decl.search(searcher)); } TypeDefinition::Protected(ref prot_decl) => { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Type(&self)) + .or_not_found()); for item in prot_decl.items.iter() { match item { ProtectedTypeDeclarativeItem::Subprogram(ref subprogram) => { @@ -633,17 +672,23 @@ impl Search for TypeDeclaration { } } TypeDefinition::Record(ref element_decls) => { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Type(&self)) + .or_not_found()); for elem in element_decls { return_if_found!(elem.subtype.search(searcher)); } } TypeDefinition::Access(ref subtype_indication) => { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Type(&self)) + .or_not_found()); return_if_found!(subtype_indication.search(searcher)); } TypeDefinition::Array(ref indexes, ref subtype_indication) => { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Type(&self)) + .or_not_found()); for index in indexes.iter() { match index { ArrayIndex::IndexSubtypeDefintion(ref type_mark) => { @@ -657,15 +702,21 @@ impl Search for TypeDeclaration { return_if_found!(subtype_indication.search(searcher)); } TypeDefinition::Subtype(ref subtype_indication) => { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Type(&self)) + .or_not_found()); return_if_found!(subtype_indication.search(searcher)); } TypeDefinition::Integer(ref range) => { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Type(&self)) + .or_not_found()); return_if_found!(range.search(searcher)); } TypeDefinition::File(ref type_mark) => { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Type(&self)) + .or_not_found()); return_if_found!(type_mark.search(searcher)); } TypeDefinition::Incomplete(ref reference) => { @@ -676,7 +727,9 @@ impl Search for TypeDeclaration { } // @TODO others _ => { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Type(&self)) + .or_not_found()); } } NotFound @@ -776,7 +829,9 @@ impl Search for WithPos { impl Search for ObjectDeclaration { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { - return_if_found!(searcher.search_decl_pos(self.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident.pos(), FoundDeclaration::Object(&self)) + .or_not_found()); return_if_found!(self.subtype_indication.search(searcher)); if let Some(ref expr) = self.expression { expr.search(searcher) @@ -825,7 +880,9 @@ impl Search for Declaration { name, signature, } = alias; - return_if_found!(searcher.search_decl_pos(&designator.pos).or_not_found()); + return_if_found!(searcher + .search_decl(&designator.pos, FoundDeclaration::Alias(&alias)) + .or_not_found()); return_if_found!(subtype_indication.search(searcher)); return_if_found!(name.search(searcher)); if let Some(signature) = signature { @@ -841,7 +898,9 @@ impl Search for Declaration { generic_list, port_list, } = component; - return_if_found!(searcher.search_decl_pos(ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(ident.pos(), FoundDeclaration::Component(&component)) + .or_not_found()); return_if_found!(generic_list.search(searcher)); return_if_found!(port_list.search(searcher)); } @@ -853,7 +912,9 @@ impl Search for Declaration { open_info, file_name, } = file; - return_if_found!(searcher.search_decl_pos(ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(ident.pos(), FoundDeclaration::File(&file)) + .or_not_found()); return_if_found!(subtype_indication.search(searcher)); return_if_found!(open_info.search(searcher)); return_if_found!(file_name.search(searcher)); @@ -870,7 +931,9 @@ impl Search for InterfaceDeclaration { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { match self { InterfaceDeclaration::Object(ref decl) => { - return_if_found!(searcher.search_decl_pos(decl.ident.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(decl.ident.pos(), FoundDeclaration::InterfaceObject(&decl)) + .or_not_found()); return_if_found!(decl.subtype_indication.search(searcher)); return_if_found!(decl.expression.search(searcher)); } @@ -896,7 +959,7 @@ impl Search for SubprogramDeclaration { impl Search for ProcedureSpecification { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { return_if_found!(searcher - .search_decl_pos(&self.designator.pos) + .search_decl(&self.designator.pos, FoundDeclaration::Procedure(&self)) .or_not_found()); self.parameter_list.search(searcher) } @@ -905,7 +968,7 @@ impl Search for ProcedureSpecification { impl Search for FunctionSpecification { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { return_if_found!(searcher - .search_decl_pos(&self.designator.pos) + .search_decl(&self.designator.pos, FoundDeclaration::Function(&self)) .or_not_found()); return_if_found!(self.parameter_list.search(searcher)); self.return_type.search(searcher) @@ -915,7 +978,9 @@ impl Search for FunctionSpecification { impl Search for LibraryClause { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { for name in self.name_list.iter() { - return_if_found!(searcher.search_decl_pos(name.pos()).or_not_found()); + return_if_found!(searcher + .search_decl(name.pos(), FoundDeclaration::Library(&name)) + .or_not_found()); } NotFound } @@ -962,7 +1027,9 @@ impl Search for EntityDeclaration { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { return_if_finished!(searcher.search_source(self.source())); return_if_found!(self.context_clause.search(searcher)); - return_if_found!(searcher.search_decl_pos(self.ident().pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident().pos(), FoundDeclaration::Entity(&self)) + .or_not_found()); return_if_found!(self.generic_clause.search(searcher)); return_if_found!(self.port_clause.search(searcher)); return_if_found!(self.decl.search(searcher)); @@ -984,7 +1051,9 @@ impl Search for PackageDeclaration { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { return_if_finished!(searcher.search_source(self.source())); return_if_found!(self.context_clause.search(searcher)); - return_if_found!(searcher.search_decl_pos(self.ident().pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident().pos(), FoundDeclaration::Package(&self)) + .or_not_found()); return_if_found!(self.generic_clause.search(searcher)); self.decl.search(searcher) } @@ -1003,7 +1072,9 @@ impl Search for PackageInstantiation { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { return_if_finished!(searcher.search_source(self.source())); return_if_found!(self.context_clause.search(searcher)); - return_if_found!(searcher.search_decl_pos(self.ident().pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident().pos(), FoundDeclaration::PackageInstance(&self)) + .or_not_found()); self.package_name.search(searcher) } } @@ -1012,7 +1083,9 @@ impl Search for ConfigurationDeclaration { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { return_if_finished!(searcher.search_source(self.source())); return_if_found!(self.context_clause.search(searcher)); - return_if_found!(searcher.search_decl_pos(self.ident().pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident().pos(), FoundDeclaration::Configuration(&self)) + .or_not_found()); self.entity_name.search(searcher) } } @@ -1020,7 +1093,9 @@ impl Search for ConfigurationDeclaration { impl Search for ContextDeclaration { fn search(&self, searcher: &mut impl Searcher) -> SearchResult { return_if_finished!(searcher.search_source(self.source())); - return_if_found!(searcher.search_decl_pos(self.ident().pos()).or_not_found()); + return_if_found!(searcher + .search_decl(self.ident().pos(), FoundDeclaration::Context(&self)) + .or_not_found()); self.items.search(searcher) } } @@ -1108,7 +1183,91 @@ impl Searcher for ItemAtCursor { } } -// Search for all reference to declaration/defintion +// Search for a declaration/definition and format it +pub struct FormatDeclaration { + decl_pos: SrcPos, + result: Option, +} + +impl FormatDeclaration { + pub fn new(decl_pos: &SrcPos) -> FormatDeclaration { + FormatDeclaration { + decl_pos: decl_pos.clone(), + result: None, + } + } + + pub fn search(searchable: &impl Search, decl_pos: &SrcPos) -> Option { + let mut searcher = Self::new(decl_pos); + let _ = searchable.search(&mut searcher); + searcher.result + } +} + +impl Searcher for FormatDeclaration { + fn search_decl(&mut self, pos: &SrcPos, decl: FoundDeclaration) -> SearchState { + if pos == &self.decl_pos { + self.result = Some(match decl { + FoundDeclaration::InterfaceObject(ref value) => match value.list_type { + InterfaceListType::Port => format!("```vhdl\nport {};\n```", value), + InterfaceListType::Generic => format!("```vhdl\ngeneric {};\n```", value), + InterfaceListType::Parameter => format!("```vhdl\n{};\n```", value), + }, + FoundDeclaration::ForIndex(ref ident, ref drange) => { + format!("```vhdl\nfor {} in {} loop\n```", ident, drange) + } + FoundDeclaration::ForGenerateIndex(ref ident, ref value) => match ident { + Some(ident) => format!("```vhdl\n{}: {}\n```", ident, value), + None => format!("```vhdl\n{}\n```", value), + }, + FoundDeclaration::Library(ref value) => { + format!("```vhdl\nlibrary {};\n```", value) + } + FoundDeclaration::Function(ref value) => { + format!("```vhdl\n{};\n```", value) + } + FoundDeclaration::Procedure(ref value) => { + format!("```vhdl\n{};\n```", value) + } + FoundDeclaration::Object(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::File(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::Type(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::Component(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::Alias(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::Package(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::PackageInstance(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::Configuration(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::Entity(ref value) => { + format!("```vhdl\n{}\n```", value) + } + FoundDeclaration::Context(ref value) => { + format!("```vhdl\n{}\n```", value) + } + }); + Finished(Found) + } else { + NotFinished + } + } +} + +// Search for all references to declaration/definition pub struct FindAllReferences { decl_pos: SrcPos, references: Vec, diff --git a/vhdl_lang/src/project.rs b/vhdl_lang/src/project.rs index 3a2e14429..0907d54e2 100644 --- a/vhdl_lang/src/project.rs +++ b/vhdl_lang/src/project.rs @@ -220,6 +220,12 @@ impl Project { self.root.search_reference(source, cursor) } + /// Search for the declaration at decl_pos and format it + pub fn format_declaration(&self, decl_pos: &SrcPos) -> Option { + self.root.format_declaration(decl_pos) + } + + /// Search for all references to the declaration at decl_pos pub fn find_all_references(&self, decl_pos: &SrcPos) -> Vec { self.root.find_all_references(decl_pos) } diff --git a/vhdl_lang/src/syntax/expression.rs b/vhdl_lang/src/syntax/expression.rs index 072bbf0e5..6a208a852 100644 --- a/vhdl_lang/src/syntax/expression.rs +++ b/vhdl_lang/src/syntax/expression.rs @@ -53,8 +53,8 @@ fn kind_to_binary_op(kind: Kind) -> Option<(Binary, usize)> { Plus => Some((Binary::Plus, 5)), Minus => Some((Binary::Minus, 5)), Concat => Some((Binary::Concat, 5)), - Times => Some((Binary::Times, 5)), + Times => Some((Binary::Times, 7)), Div => Some((Binary::Div, 7)), Mod => Some((Binary::Mod, 7)), Rem => Some((Binary::Rem, 7)), @@ -1140,6 +1140,13 @@ mod tests { assert_expression_is("1-2-3", "((Integer(1) Minus Integer(2)) Minus Integer(3))"); + assert_expression_is("1+2*3", "(Integer(1) Plus (Integer(2) Times Integer(3)))"); + + assert_expression_is("(1+2)*3", "((Integer(1) Plus Integer(2)) Times Integer(3))"); + + // Multiplication has precedence over negation. + assert_expression_is("-1 * 2", "(Minus (Integer(1) Times Integer(2)))"); + assert_expression_is("not 1 + 2", "((Not Integer(1)) Plus Integer(2))"); assert_expression_is("abs not 1 + 2", "((Abs (Not Integer(1))) Plus Integer(2))"); diff --git a/vhdl_lang/src/syntax/interface_declaration.rs b/vhdl_lang/src/syntax/interface_declaration.rs index 1f807aa5a..983ecf3dd 100644 --- a/vhdl_lang/src/syntax/interface_declaration.rs +++ b/vhdl_lang/src/syntax/interface_declaration.rs @@ -144,6 +144,7 @@ fn parse_interface_object_declaration( .into_iter() .map(|ident| { InterfaceDeclaration::Object(InterfaceObjectDeclaration { + list_type, mode, class: object_class, ident, @@ -258,13 +259,6 @@ fn parse_semicolon_separator(stream: &mut TokenStream) -> ParseResult<()> { Ok(()) } -#[derive(PartialEq, Clone, Copy)] -enum InterfaceListType { - Port, - Generic, - Parameter, -} - fn is_sync_kind(list_type: InterfaceListType, kind: Kind) -> bool { matches!( (list_type, kind), @@ -405,6 +399,7 @@ mod tests { code.with_stream_no_diagnostics(parse_generic_interface_list), vec![ InterfaceDeclaration::Object(InterfaceObjectDeclaration { + list_type: InterfaceListType::Generic, mode: Mode::In, class: ObjectClass::Constant, ident: code.s1("foo").ident(), @@ -412,6 +407,7 @@ mod tests { expression: None }), InterfaceDeclaration::Object(InterfaceObjectDeclaration { + list_type: InterfaceListType::Generic, mode: Mode::In, class: ObjectClass::Constant, ident: code.s1("bar").ident(), @@ -428,6 +424,7 @@ mod tests { assert_eq!( code.with_stream(parse_generic), InterfaceDeclaration::Object(InterfaceObjectDeclaration { + list_type: InterfaceListType::Generic, mode: Mode::In, class: ObjectClass::Constant, ident: code.s1("foo").ident(), @@ -479,6 +476,7 @@ mod tests { assert_eq!( code.with_stream(parse_port), InterfaceDeclaration::Object(InterfaceObjectDeclaration { + list_type: InterfaceListType::Port, mode: Mode::In, class: ObjectClass::Signal, ident: code.s1("foo").ident(), @@ -546,8 +544,8 @@ mod tests { assert_eq!( code.with_stream(parse_generic), InterfaceDeclaration::Object(InterfaceObjectDeclaration { + list_type: InterfaceListType::Generic, mode: Mode::In, - class: ObjectClass::Constant, ident: code.s1("foo").ident(), subtype_indication: code.s1("std_logic").subtype_indication(), @@ -562,8 +560,8 @@ mod tests { assert_eq!( code.with_stream(parse_port), InterfaceDeclaration::Object(InterfaceObjectDeclaration { + list_type: InterfaceListType::Port, mode: Mode::In, - class: ObjectClass::Signal, ident: code.s1("foo").ident(), subtype_indication: code.s1("std_logic").subtype_indication(), diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index bf5b3241a..78ad2bf76 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -4,19 +4,23 @@ // // Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com +use super::alias_declaration::parse_alias_declaration; use super::common::ParseResult; +use super::component_declaration::parse_component_declaration; use super::concurrent_statement::parse_labeled_concurrent_statement; use super::context::{parse_library_clause, parse_use_clause}; use super::declarative_part::parse_declarative_part_leave_end_token; -use super::design_unit::parse_design_file; +use super::design_unit::{parse_design_file, parse_entity_declaration}; use super::expression::{parse_aggregate, parse_choices, parse_expression}; use super::interface_declaration::{parse_generic, parse_parameter, parse_port}; use super::names::{parse_association_list, parse_designator, parse_name, parse_selected_name}; +use super::object_declaration::{parse_file_declaration, parse_object_declaration}; 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::type_declaration::parse_type_declaration; use super::waveform::parse_waveform; use crate::ast; use crate::ast::*; @@ -334,6 +338,30 @@ impl Code { self.symbols.symtab().insert_utf8(name) } + pub fn type_decl(&self) -> TypeDeclaration { + self.with_stream_no_diagnostics(parse_type_declaration) + } + + pub fn object_decl(&self) -> ObjectDeclaration { + self.parse_ok(parse_object_declaration).remove(0) + } + + pub fn file_decl(&self) -> FileDeclaration { + self.parse_ok(parse_file_declaration).remove(0) + } + + pub fn alias_decl(&self) -> AliasDeclaration { + self.parse_ok(parse_alias_declaration) + } + + pub fn component_decl(&self) -> ComponentDeclaration { + self.with_stream_no_diagnostics(parse_component_declaration) + } + + pub fn entity_decl(&self) -> EntityDeclaration { + self.with_stream_no_diagnostics(parse_entity_declaration) + } + pub fn subtype_indication(&self) -> SubtypeIndication { self.parse_ok(parse_subtype_indication) } diff --git a/vhdl_ls/src/stdio_server.rs b/vhdl_ls/src/stdio_server.rs index 3a2d91519..91a4d63b9 100644 --- a/vhdl_ls/src/stdio_server.rs +++ b/vhdl_ls/src/stdio_server.rs @@ -152,6 +152,14 @@ impl ConnectionRpcChannel { } Err(request) => request, }; + let request = match extract::(request) { + Ok((id, params)) => { + let result = server.text_document_hover(¶ms); + self.send_response(lsp_server::Response::new_ok(id, result)); + return; + } + Err(request) => request, + }; let request = match extract::(request) { Ok((id, params)) => { let result = server.text_document_references(¶ms); diff --git a/vhdl_ls/src/vhdl_server.rs b/vhdl_ls/src/vhdl_server.rs index 46d86b140..b24bc01a2 100644 --- a/vhdl_ls/src/vhdl_server.rs +++ b/vhdl_ls/src/vhdl_server.rs @@ -199,6 +199,11 @@ impl VHDLServer { self.mut_server().text_document_definition(¶ms) } + // textDocument/hover + pub fn text_document_hover(&mut self, params: &TextDocumentPositionParams) -> Option { + self.mut_server().text_document_hover(¶ms) + } + // textDocument/references pub fn text_document_references(&mut self, params: &ReferenceParams) -> Vec { self.mut_server().text_document_references(¶ms) @@ -248,6 +253,7 @@ impl InitializedVHDLServer { )), declaration_provider: Some(true), definition_provider: Some(true), + hover_provider: Some(true), references_provider: Some(true), ..Default::default() }; @@ -398,6 +404,23 @@ impl InitializedVHDLServer { self.text_document_declaration(params) } + pub fn text_document_hover(&mut self, params: &TextDocumentPositionParams) -> Option { + self.project + .get_source(&uri_to_file_name(¶ms.text_document.uri)) + .and_then(|source| { + self.project + .search_reference(&source, from_lsp_pos(params.position)) + }) + .and_then(|decl_pos| self.project.format_declaration(&decl_pos)) + .map(|result| Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: result, + }), + range: None, + }) + } + pub fn text_document_references(&mut self, params: &ReferenceParams) -> Vec { let decl_pos = self .project