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 +21 -0
- package/package.json +1 -1
- package/scripts/benchmark-search-docs.py +11 -2
- package/src/Revit/Tools/SearchApiDocs.cs +98 -5
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|