formatter: Keep space before # in unqualified typedef

A parameterized type in a typedef lost the space before '#':

  typedef dv_base_env_cov #(.CFG_T(tl_agent_env_cfg)) tl_agent_env_cov;

was formatted as:

  typedef dv_base_env_cov#(.CFG_T(tl_agent_env_cfg)) tl_agent_env_cov;

The "no space before #" rule in SpacesRequiredBetween() fires whenever the
'#' follows an identifier whose direct parent is kUnqualifiedId, unless the
context appears in an exemption list. A typedef was not exempted.

Package-qualified types are deliberately left alone, since the existing
tests require the "type#(params...)::method(...)" spelling:

  typedef foo_pkg::baz_t#(.L(L), .W(W)) bar_t;

The two cases are distinguished by kQualifiedId, which only wraps the
kUnqualifiedId in the qualified form.

The exemption is a separate IsInsideFirst() call rather than a fifth entry
in the existing list: MatchesTagAnyOf() unrolls at most four tags and
aborts at runtime beyond that.

Adds formatter tests covering a typedef that already has the space, one
that does not, and a short single-parameter case that stays on one line.

Fixes #850
diff --git a/verible/verilog/formatting/formatter_test.cc b/verible/verilog/formatting/formatter_test.cc
index e885d74..cfd0185 100644
--- a/verible/verilog/formatting/formatter_test.cc
+++ b/verible/verilog/formatting/formatter_test.cc
@@ -4343,6 +4343,19 @@
      "    .L(L),\n"
      "    .W(W)\n"
      ") bar_t;\n"},
+    // unqualified parameterized type keeps a space before '#'
+    {"typedef dv_base_env_cov #(.CFG_T(tl_agent_env_cfg)) tl_agent_env_cov;\n",
+     "typedef dv_base_env_cov #(\n"
+     "    .CFG_T(tl_agent_env_cfg)\n"
+     ") tl_agent_env_cov;\n"},
+    // ... and is inserted when absent
+    {"typedef dv_base_env_cov#(.CFG_T(tl_agent_env_cfg)) tl_agent_env_cov;\n",
+     "typedef dv_base_env_cov #(\n"
+     "    .CFG_T(tl_agent_env_cfg)\n"
+     ") tl_agent_env_cov;\n"},
+    // single short parameter stays on one line
+    {"typedef my_class #(.P(P)) my_class_t;\n",
+     "typedef my_class #(.P(P)) my_class_t;\n"},
 
     // package test cases
     {"package fedex;localparam  int  www=3 ;endpackage   :  fedex\n",
diff --git a/verible/verilog/formatting/token-annotator.cc b/verible/verilog/formatting/token-annotator.cc
index d4af24a..fcbbdbb 100644
--- a/verible/verilog/formatting/token-annotator.cc
+++ b/verible/verilog/formatting/token-annotator.cc
@@ -500,11 +500,23 @@
     // This may be controversial or context-dependent, as parameterized
     // classes often appear with method calls like:
     //   type#(params...)::method(...);
+    // A parameterized type in a typedef keeps the space before '#':
+    //   typedef my_class #(.P(P)) my_class_t;
+    // but a package-qualified type does not, matching the existing
+    // "type#(params...)::method(...)" convention:
+    //   typedef pkg::my_class#(.P(P)) my_class_t;
+    // Kept as separate IsInsideFirst() calls because MatchesTagAnyOf()
+    // only unrolls up to four tags.
+    const bool inside_unqualified_typedef =
+        left_context.IsInsideFirst({NodeEnum::kTypeDeclaration}, {}) &&
+        !left_context.IsInsideFirst({NodeEnum::kQualifiedId}, {});
+
     if (left_context.DirectParentIs(NodeEnum::kUnqualifiedId) &&
         !left_context.IsInsideFirst(
             {NodeEnum::kInstantiationType, NodeEnum::kBindTargetInstance,
              NodeEnum::kExtendsList, NodeEnum::kBraceGroup},
-            {})) {
+            {}) &&
+        !inside_unqualified_typedef) {
       return {0, "No space before # when direct parent is kUnqualifiedId."};
     }
     return {1, "Spaces before # in most other contexts."};