pi-revit 0.2.10 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ Format follows [Keep a Changelog](https://keepachangelog.com/); version headers
7
7
  Every published version gets an entry with **Added** / **Changed** / **Fixed** sections
8
8
  describing what the user will notice — not internal refactors.
9
9
 
10
+ ## [0.2.11] - 2026-07-22
11
+
12
+ ### Fixed
13
+ - `search_api_docs`: signature queries now accept .NET type names and qualified names —
14
+ `Wall.Create(Document, Curve, ElementId, Boolean` and `...(System.String` match the
15
+ rendered `bool` / `string`, and `(Autodesk.Revit.DB.Document` matches `Document`.
16
+ Parameter types in the query are reduced exactly the way the index renders them
17
+ (namespace stripped, CLR name → C# keyword, case-insensitive).
18
+ - `search_api_docs`: Creation-factory calls resolve — `Document.Create.NewRoom(Level, UV`
19
+ finds `Document.NewRoom`, and `Document.Create.NewFamilyInstance` finds the
20
+ `ItemFactoryBase` overloads (factory members documented on a base class). A note in the
21
+ result explains the rewrite. Applies only to the literal `Document.Create.` /
22
+ `Application.Create.` prefixes; ordinary members like `Wall.Create` are untouched, and
23
+ nonsense like `Document.Create.Banana` still honestly returns nothing.
24
+
25
+ Both were observed live: pi stumbled on these five times across two modeling sessions.
26
+ The benchmark gained six regression probes for them.
27
+
28
+ Requires redeploying the Revit add-in (`scripts\deploy.ps1` with Revit closed, then
29
+ restart Revit).
30
+
10
31
  ## [0.2.10] - 2026-07-22
11
32
 
12
33
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-revit",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "Native Pi connector for Autodesk Revit. Run npx.cmd -y pi-revit for the full Windows install.",
5
5
  "author": "Ahmad Altahlawi",
6
6
  "license": "MIT",
@@ -80,7 +80,8 @@ class Member:
80
80
  @property
81
81
  def short_sig_prefix(s):
82
82
  shorts = [short_type(p) for p in s.params]
83
- return f"{s.typename}.{s.member}(" + ", ".join(shorts)
83
+ head = s.typename if s.member == "#ctor" else f"{s.typename}.{s.member}"
84
+ return f"{head}(" + ", ".join(shorts)
84
85
 
85
86
  def text_of(el):
86
87
  if el is None: return None
@@ -161,7 +162,8 @@ for m in sampleB:
161
162
  matches, _ = search(canonical)
162
163
  B["n"] += 1
163
164
  sigs = [x.get("signature","") for x in matches]
164
- ok = bool(sigs and sigs[0].startswith(f"{m.typename}.{m.member}("))
165
+ want = f"new {m.typename}(" if m.member == "#ctor" else f"{m.typename}.{m.member}("
166
+ ok = bool(sigs and sigs[0].startswith(want))
165
167
  B["top1_sig"] += ok
166
168
  variants = [canonical.replace(", ", ","),
167
169
  canonical.replace(", ", " , "),
@@ -200,6 +202,13 @@ probe("case-insensitive", "wall.create(document, curve")
200
202
  probe("Double vs double", "UnitUtils.ConvertToInternalUnits(Double, ForgeTypeId")
201
203
  probe("trailing garbage brackets", "Wall.Create(Document, Curve]]", True) # observational
202
204
  probe("double periods", "Wall..Create", True) # observational
205
+ # 0.2.11: CLR alias + namespace reduction in signature queries, Creation-factory rewrite
206
+ probe("CLR alias Boolean", "Wall.Create(Document, Curve, ElementId, Boolean")
207
+ probe("qualified param type", "Wall.Create(Autodesk.Revit.DB.Document, Curve")
208
+ probe("System-qualified alias", "ParameterFilterElement.Create(Document, System.String")
209
+ probe("factory rewrite", "Document.Create.NewRoom(Level, UV")
210
+ probe("factory inherited member", "Document.Create.NewFamilyInstance")
211
+ probe("factory honesty control", "Document.Create.Banana", expect_nonzero=False)
203
212
  print("C done", flush=True)
204
213
  R["C complex-syntax"] = C
205
214
 
@@ -81,7 +81,7 @@ namespace RevitBridge.Tools
81
81
  if (index.Members.Count == 0)
82
82
  throw new InvalidOperationException("The Revit API documentation index is empty. " + string.Join(" ", index.Warnings));
83
83
 
84
- var (top, total) = Search(index, query, kindFilter, maxResults);
84
+ var (top, total, rewriteNote) = Search(index, query, kindFilter, maxResults);
85
85
 
86
86
  var matches = new List<Dictionary<string, object?>>(top.Count);
87
87
  for (int i = 0; i < top.Count; i++)
@@ -116,22 +116,56 @@ namespace RevitBridge.Tools
116
116
  indexedMembers = index.Members.Count,
117
117
  sources = index.SourceFiles,
118
118
  warnings = index.Warnings.Count > 0 ? index.Warnings : null,
119
- }, BuildMarkdown(query, top, total, index));
119
+ }, BuildMarkdown(query, top, total, index) + (rewriteNote is null ? string.Empty : $"\nNote: {rewriteNote}"));
120
120
  }
121
121
 
122
122
  // ----------------------------------------------------------------- search
123
123
 
124
124
  private static readonly char[] WordSeparators = { '.', ' ', '_', '(', ')', ',', ':', '-', '/' };
125
125
 
126
- private static (List<ApiMember> Top, int Total) Search(DocIndex index, string query, char? kindFilter, int maxResults)
126
+ private static (List<ApiMember> Top, int Total, string? RewriteNote) Search(DocIndex index, string query, char? kindFilter, int maxResults)
127
127
  {
128
128
  // Signatures are rendered exactly one way ("Name(Type, Type)"): normalize the
129
129
  // query's spacing around commas and parentheses so 'Wall.Create(Document,Curve'
130
130
  // and 'Wall.Create( Document, Curve' hit the same rendered text instead of
131
- // failing on typography.
132
- string q = query.ToLowerInvariant();
131
+ // failing on typography. Parameter types are additionally reduced the same way
132
+ // the renderer reduces them (namespaces stripped, CLR names -> C# keywords), so
133
+ // 'Wall.Create(Document, Curve, ElementId, Boolean' and
134
+ // '...(System.String' match the rendered 'bool' / 'string'.
135
+ string q = NormalizeSignatureQuery(query).ToLowerInvariant();
133
136
  q = Regex.Replace(q, @"\s*,\s*", ", ");
134
137
  q = Regex.Replace(q, @"\(\s+", "(");
138
+
139
+ // The Creation-factory pattern: code says doc.Create.NewRoom(...) but the docs
140
+ // live on Autodesk.Revit.Creation.Document (rendered 'Document.NewRoom') or a
141
+ // base class like ItemFactoryBase. Try the factory rewrite, then the bare
142
+ // member, before giving up. Deterministic rewrites of an exact idiom — never
143
+ // applied unless the literal 'document.create.' / 'application.create.' prefix
144
+ // is present, so ordinary names like Wall.Create are untouched.
145
+ var candidates = new List<(string Query, string? Note)> { (q, null) };
146
+ foreach (string factory in new[] { "document.create.", "application.create." })
147
+ {
148
+ if (!q.StartsWith(factory, StringComparison.Ordinal))
149
+ continue;
150
+ string owner = factory[..(factory.IndexOf('.') + 1)]; // "document."
151
+ string rest = q[factory.Length..];
152
+ candidates.Add((owner + rest, $"'{owner}Create.*' is the Creation factory — matched as '{owner}{rest}'."));
153
+ string bareMember = rest.Split('(')[0];
154
+ if (bareMember.Length > 0)
155
+ candidates.Add((bareMember, $"'{owner}Create.{bareMember}' is a Creation-factory call; its docs live on the factory class (e.g. ItemFactoryBase) — matched by member name '{bareMember}'."));
156
+ }
157
+
158
+ foreach (var (candidate, note) in candidates)
159
+ {
160
+ var (top, total) = RunScoring(index, candidate, kindFilter, maxResults);
161
+ if (total > 0)
162
+ return (top, total, note);
163
+ }
164
+ return (new List<ApiMember>(), 0, null);
165
+ }
166
+
167
+ private static (List<ApiMember> Top, int Total) RunScoring(DocIndex index, string q, char? kindFilter, int maxResults)
168
+ {
135
169
  string[] words = q.Split(WordSeparators, StringSplitOptions.RemoveEmptyEntries);
136
170
 
137
171
  var scored = new List<(ApiMember Member, int Score)>();
@@ -155,6 +189,65 @@ namespace RevitBridge.Tools
155
189
  return (top, scored.Count);
156
190
  }
157
191
 
192
+ /// <summary>Reduces the parameter part of a signature query exactly the way the
193
+ /// renderer reduces signatures: each identifier keeps only its last dot-segment
194
+ /// and CLR primitive names map to C# keywords (case-insensitively — queries say
195
+ /// 'Boolean' or 'boolean'; signatures render 'bool'). The member path before the
196
+ /// first '(' is left untouched.</summary>
197
+ private static string NormalizeSignatureQuery(string query)
198
+ {
199
+ int paren = query.IndexOf('(');
200
+ if (paren < 0)
201
+ return query;
202
+
203
+ string tail = query[(paren + 1)..];
204
+ var result = new StringBuilder(tail.Length);
205
+ int i = 0;
206
+ while (i < tail.Length)
207
+ {
208
+ char c = tail[i];
209
+ if (char.IsLetter(c) || c == '_')
210
+ {
211
+ int start = i, lastDot = -1;
212
+ while (i < tail.Length && (char.IsLetterOrDigit(tail[i]) || tail[i] is '_' or '.'))
213
+ {
214
+ if (tail[i] == '.')
215
+ lastDot = i;
216
+ i++;
217
+ }
218
+ string identifier = tail[(lastDot >= 0 ? lastDot + 1 : start)..i];
219
+ result.Append(QueryTypeAliases.TryGetValue(identifier, out string? keyword) ? keyword : identifier);
220
+ }
221
+ else
222
+ {
223
+ result.Append(c);
224
+ i++;
225
+ }
226
+ }
227
+ return query[..(paren + 1)] + result;
228
+ }
229
+
230
+ /// <summary>Case-insensitive inverse of MapTypeKeyword for query text.</summary>
231
+ private static readonly Dictionary<string, string> QueryTypeAliases = new(StringComparer.OrdinalIgnoreCase)
232
+ {
233
+ ["String"] = "string",
234
+ ["Boolean"] = "bool",
235
+ ["Int32"] = "int",
236
+ ["Int64"] = "long",
237
+ ["Int16"] = "short",
238
+ ["Double"] = "double",
239
+ ["Single"] = "float",
240
+ ["Object"] = "object",
241
+ ["Void"] = "void",
242
+ ["Byte"] = "byte",
243
+ ["SByte"] = "sbyte",
244
+ ["Char"] = "char",
245
+ ["Decimal"] = "decimal",
246
+ ["UInt16"] = "ushort",
247
+ ["UInt32"] = "uint",
248
+ ["UInt64"] = "ulong",
249
+ };
250
+
158
251
  private static int Score(ApiMember member, string q, string[] words)
159
252
  {
160
253
  // A query with a parenthesis targets a specific overload by signature,