autocompletePaths method
Autocomplete suggestions for a partial hierarchical path.
The partial path is split into segments. Completed segments navigate
down the tree; the final (possibly empty) segment is used as a prefix
filter on children at that level. Returns up to limit full paths
(with / appended for nodes that have children).
Implementation
List<String> autocompletePaths(String partialPath, {int? limit}) {
final effectiveLimit = limit ?? defaultHierarchySearchLimit;
final normalized = partialPath.replaceAll('.', hierarchyPathSeparator);
final endsWithSep = normalized.endsWith(hierarchyPathSeparator);
final parts = _splitPath(partialPath);
// Navigate to the deepest complete segment.
var current = root;
final completedParts = <String>[root.name];
final navParts = endsWithSep || parts.isEmpty
? parts
: parts.sublist(0, parts.length - 1);
for (final seg in navParts) {
// If the segment matches the current node name, stay at this level
// (handles the root name appearing as the first path segment).
if (current.name == seg) {
continue;
}
final child = current.children.where((c) => c.name == seg).firstOrNull;
if (child == null) {
return const [];
}
current = child;
completedParts.add(child.name);
}
// The trailing prefix to filter on (empty if path ends with separator).
final prefix = (endsWithSep || parts.isEmpty) ? '' : parts.last;
final suggestions = <String>[];
// When the prefix matches the current (root-level) node itself and we
// haven't navigated past it, suggest the root path so that typing a
// partial root name produces a completion.
if (prefix.isNotEmpty &&
completedParts.length == 1 &&
current == root &&
current.name.startsWith(prefix)) {
final rootPath = current.name;
suggestions.add(current.children.isNotEmpty
? '$rootPath$hierarchyPathSeparator'
: rootPath);
}
for (final child in current.children) {
if (prefix.isEmpty || child.name.startsWith(prefix)) {
final pathParts = [...completedParts, child.name];
final path = pathParts.join(hierarchyPathSeparator);
suggestions.add(
child.children.isNotEmpty ? '$path$hierarchyPathSeparator' : path);
if (suggestions.length >= effectiveLimit) {
break;
}
}
}
return suggestions;
}