pi-revit 0.1.8 → 0.1.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-revit",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
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",
@@ -4,6 +4,7 @@ using System.Text.Json;
4
4
  using System.Xml;
5
5
  using System.Xml.Linq;
6
6
  using Autodesk.Revit.DB;
7
+ using Autodesk.Revit.UI;
7
8
 
8
9
  namespace RevitBridge.Tools
9
10
  {
@@ -22,13 +23,14 @@ namespace RevitBridge.Tools
22
23
  private const int MaxResultsCap = 50;
23
24
  private const int MaxMarkdownChars = 6_000;
24
25
  private const int MaxSummaryChars = 600;
26
+ private const int MaxRemarksChars = 600;
25
27
  private const int MaxReturnsChars = 300;
26
28
  private const int MaxParamDocChars = 240;
27
29
  private const int MaxParamsPerMember = 10;
28
30
 
29
31
  public string Name => "search_api_docs";
30
32
  public string Label => "Search API Docs";
31
- public string Description => "Search the offline Revit API documentation (the RevitAPI.xml and RevitAPIUI.xml files shipped with Revit) for types, methods, constructors, properties, fields (including enum values such as BuiltInParameter names), and events. query is a single name or substring — e.g. 'FilteredElementCollector', 'Wall.Create', 'WALL_BASE_OFFSET', 'ElementTransformUtils' — ranked: exact name first, then prefix, then substring; dotted Type.Member queries match composites. Returns signatures with summary, parameter docs, return docs, and the Revit version a member was introduced in ('since'). Works with no document open. Use it to verify exact classes, members, and signatures before writing execute_csharp code. The first query builds the index (a few seconds); later queries are instant.";
33
+ public string Description => "Search the offline Revit API documentation (the RevitAPI.xml and RevitAPIUI.xml files shipped with Revit) for types, methods, constructors, properties, fields, and events; every public API enum is fully searchable by value name (values the XML leaves undocumented are synthesized from the API assemblies). query is a single name or substring — e.g. 'FilteredElementCollector', 'Wall.Create', 'WALL_BASE_OFFSET' — ranked: exact name first, then prefix, then substring; dotted Type.Member queries match composites. Returns signatures with summary, remarks, parameter docs, return docs, and the Revit version a member was introduced in ('since'). Works with no document open. Use it to verify exact classes, members, and signatures before writing execute_csharp code. The first query builds the index (a few seconds); later queries are instant.";
32
34
 
33
35
  public bool RequiresDocument => false;
34
36
 
@@ -90,6 +92,7 @@ namespace RevitBridge.Tools
90
92
  ["assembly"] = member.Assembly,
91
93
  ["since"] = member.Since,
92
94
  ["summary"] = member.Summary,
95
+ ["remarks"] = member.Remarks,
93
96
  ["parameters"] = member.Parameters?
94
97
  .Select(pair => new Dictionary<string, object?> { ["name"] = pair.Key, ["description"] = pair.Value })
95
98
  .ToList(),
@@ -177,6 +180,8 @@ namespace RevitBridge.Tools
177
180
  break;
178
181
  }
179
182
  markdown.Append(line);
183
+ if (i == 0)
184
+ AppendTopMatchDocs(markdown, member);
180
185
  }
181
186
  }
182
187
  foreach (string warning in index.Warnings)
@@ -184,6 +189,27 @@ namespace RevitBridge.Tools
184
189
  return markdown.ToString();
185
190
  }
186
191
 
192
+ /// <summary>The model sees only this markdown — details.payload never reaches it —
193
+ /// so the top match carries its remarks, parameter, and return docs inline. Lines
194
+ /// that would blow the markdown budget are dropped individually; narrowing the
195
+ /// query promotes any other match to the top slot with its docs.</summary>
196
+ private static void AppendTopMatchDocs(StringBuilder markdown, ApiMember member)
197
+ {
198
+ var lines = new List<string>(3);
199
+ if (member.Remarks is { } remarks)
200
+ lines.Add($"\n remarks: {remarks}");
201
+ if (member.Parameters is { Count: > 0 } parameters)
202
+ lines.Add($"\n params: {string.Join("; ", parameters.Select(pair => $"{pair.Key} — {pair.Value}"))}");
203
+ if (member.Returns is { } returns)
204
+ lines.Add($"\n returns: {returns}");
205
+ foreach (string line in lines)
206
+ {
207
+ if (markdown.Length + line.Length > MaxMarkdownChars)
208
+ break;
209
+ markdown.Append(line);
210
+ }
211
+ }
212
+
187
213
  private static string KindLabel(ApiMember member) => member.IsConstructor ? "constructor" : member.Kind switch
188
214
  {
189
215
  'T' => "type",
@@ -237,6 +263,7 @@ namespace RevitBridge.Tools
237
263
  public required string CompositeLower { get; init; }
238
264
  public required string ShortNameLower { get; init; }
239
265
  public string? Summary { get; init; }
266
+ public string? Remarks { get; init; }
240
267
  public string? Returns { get; init; }
241
268
  public string? Since { get; init; }
242
269
  public IReadOnlyList<KeyValuePair<string, string>>? Parameters { get; init; }
@@ -259,7 +286,7 @@ namespace RevitBridge.Tools
259
286
  var sources = new List<string>();
260
287
  var warnings = new List<string>();
261
288
 
262
- foreach (string path in CandidateXmlFiles())
289
+ foreach (string path in CandidateXmlFiles(warnings))
263
290
  {
264
291
  if (!File.Exists(path))
265
292
  {
@@ -277,55 +304,77 @@ namespace RevitBridge.Tools
277
304
  }
278
305
  }
279
306
 
280
- AddBuiltInCategoryValues(members, warnings);
307
+ AddUndocumentedEnumValues(members, warnings);
281
308
 
282
309
  return new DocIndex { Members = members, SourceFiles = sources, Warnings = warnings };
283
310
  }
284
311
 
285
- /// <summary>Autodesk's XML documents only a handful of the ~1000 BuiltInCategory
286
- /// values. Synthesize the rest from the loaded RevitAPI.dll via reflection —
287
- /// pure metadata, safe off the Revit thread.</summary>
288
- private static void AddBuiltInCategoryValues(List<ApiMember> members, List<string> warnings)
312
+ /// <summary>Autodesk's XML leaves many public API enums partially or entirely
313
+ /// undocumented (measured on Revit 2025: 107 of 669, from BuiltInCategory's ~1200
314
+ /// values down to small flags). Synthesize the missing values from the loaded API
315
+ /// assemblies via reflection — pure metadata, safe off the Revit thread.</summary>
316
+ private static void AddUndocumentedEnumValues(List<ApiMember> members, List<string> warnings)
289
317
  {
290
- try
291
- {
292
- var existing = new HashSet<string>(
293
- members
294
- .Where(member => member.FullName.StartsWith("Autodesk.Revit.DB.BuiltInCategory.", StringComparison.Ordinal))
295
- .Select(member => member.FullName),
296
- StringComparer.Ordinal);
318
+ var existing = new HashSet<string>(
319
+ members.Where(member => member.Kind == 'F').Select(member => member.FullName),
320
+ StringComparer.Ordinal);
297
321
 
298
- foreach (string name in Enum.GetNames(typeof(Autodesk.Revit.DB.BuiltInCategory)))
322
+ var assemblies = new (Func<System.Reflection.Assembly> Load, string Label)[]
323
+ {
324
+ (() => typeof(Document).Assembly, "RevitAPI"),
325
+ (() => typeof(UIApplication).Assembly, "RevitAPIUI"),
326
+ };
327
+ foreach (var (load, label) in assemblies)
328
+ {
329
+ try
299
330
  {
300
- string fullName = "Autodesk.Revit.DB.BuiltInCategory." + name;
301
- if (!existing.Add(fullName))
302
- continue;
303
-
304
- string composite = "BuiltInCategory." + name;
305
- members.Add(new ApiMember
331
+ foreach (var type in load().GetExportedTypes())
306
332
  {
307
- Kind = 'F',
308
- IsConstructor = false,
309
- Assembly = "RevitAPI",
310
- FullName = fullName,
311
- Composite = composite,
312
- Signature = composite,
313
- FullNameLower = fullName.ToLowerInvariant(),
314
- CompositeLower = composite.ToLowerInvariant(),
315
- ShortNameLower = name.ToLowerInvariant(),
316
- Summary = "BuiltInCategory enum value — usable as the 'category' argument of get_elements/get_element_types and with FilteredElementCollector.OfCategory in execute_csharp.",
317
- });
333
+ if (type.IsEnum)
334
+ AddEnumValues(type, label, existing, members);
335
+ }
336
+ }
337
+ catch (Exception ex)
338
+ {
339
+ warnings.Add($"Could not enumerate {label} enum values: {ex.Message}");
318
340
  }
319
341
  }
320
- catch (Exception ex)
342
+ }
343
+
344
+ /// <summary>Adds every value of one enum that the XML did not document itself.</summary>
345
+ private static void AddEnumValues(Type enumType, string assemblyName, HashSet<string> existing, List<ApiMember> members)
346
+ {
347
+ string typeName = enumType.Name;
348
+ string fullPrefix = (enumType.FullName ?? typeName).Replace('+', '.');
349
+ foreach (string name in Enum.GetNames(enumType))
321
350
  {
322
- warnings.Add($"Could not enumerate BuiltInCategory values: {ex.Message}");
351
+ string fullName = fullPrefix + "." + name;
352
+ if (!existing.Add(fullName))
353
+ continue;
354
+
355
+ string composite = typeName + "." + name;
356
+ members.Add(new ApiMember
357
+ {
358
+ Kind = 'F',
359
+ IsConstructor = false,
360
+ Assembly = assemblyName,
361
+ FullName = fullName,
362
+ Composite = composite,
363
+ Signature = composite,
364
+ FullNameLower = fullName.ToLowerInvariant(),
365
+ CompositeLower = composite.ToLowerInvariant(),
366
+ ShortNameLower = name.ToLowerInvariant(),
367
+ Summary = enumType == typeof(BuiltInCategory)
368
+ ? "BuiltInCategory enum value — usable as the 'category' argument of get_elements/get_element_types and with FilteredElementCollector.OfCategory in execute_csharp."
369
+ : $"{typeName} enum value (not documented in the XML; synthesized from {assemblyName} metadata).",
370
+ });
323
371
  }
324
372
  }
325
373
 
326
- /// <summary>RevitAPI.xml + RevitAPIUI.xml next to the loaded RevitAPI.dll, with the
327
- /// standard install directory as the fallback.</summary>
328
- private static IEnumerable<string> CandidateXmlFiles()
374
+ /// <summary>RevitAPI.xml + RevitAPIUI.xml next to the loaded RevitAPI.dll. When the
375
+ /// assembly location cannot be resolved, indexing is skipped with a warning instead
376
+ /// of guessing at an install directory for a Revit version that was never detected.</summary>
377
+ private static IEnumerable<string> CandidateXmlFiles(List<string> warnings)
329
378
  {
330
379
  string? installDir = null;
331
380
  try
@@ -336,9 +385,13 @@ namespace RevitBridge.Tools
336
385
  }
337
386
  catch
338
387
  {
339
- // Fall through to the standard path.
388
+ // Handled below together with the empty-location case.
389
+ }
390
+ if (string.IsNullOrEmpty(installDir))
391
+ {
392
+ warnings.Add("Could not resolve the Revit install directory from the loaded RevitAPI.dll; documentation indexing skipped.");
393
+ yield break;
340
394
  }
341
- installDir = string.IsNullOrEmpty(installDir) ? @"C:\Program Files\Autodesk\Revit 2025" : installDir;
342
395
  yield return Path.Combine(installDir, "RevitAPI.xml");
343
396
  yield return Path.Combine(installDir, "RevitAPIUI.xml");
344
397
  }
@@ -446,6 +499,7 @@ namespace RevitBridge.Tools
446
499
  CompositeLower = composite.ToLowerInvariant(),
447
500
  ShortNameLower = shortName.ToLowerInvariant(),
448
501
  Summary = Cap(CleanDocText(element.Element("summary")), MaxSummaryChars),
502
+ Remarks = Cap(CleanDocText(element.Element("remarks")), MaxRemarksChars),
449
503
  Returns = Cap(CleanDocText(element.Element("returns")), MaxReturnsChars),
450
504
  Since = CleanDocText(element.Element("since")),
451
505
  Parameters = parameters,