public void Help(ScriptExpression expression = null) { var name = expression?.ToString(); if (name != null) { if (Descriptors.TryGetValue(name, out var descriptor)) { WriteHelpForDescriptor(descriptor); return; } throw new ArgumentException($"The builtin function `{name}` does not exist", nameof(expression)); } WriteHighlightLine($"# help [name]"); // Verify that all function/modules belong to a category var invalidRegistered = Descriptors.FirstOrDefault(x => x.Value.Category == null); if (invalidRegistered.Value != null) { throw new InvalidOperationException($"The function or module `{invalidRegistered.Key}` doesn't have a category. This is an invalid state of the program"); } var categoryToDescriptors = Descriptors.GroupBy(x => x.Value.Category).ToDictionary(x => x.Key, y => y.Select(x => x.Value).Distinct().ToList()); WriteHighlightLine($"#"); foreach (var categoryPair in categoryToDescriptors.OrderBy(x => x.Key)) { var list = categoryPair.Value; // Exclude from the list modules that have been already imported var names = list.SelectMany(x => x.Names).Where(funcName => Builtins.TryGetValue(funcName, out var funcObj) && (!(funcObj is KalkModuleWithFunctions module) || !module.IsImported) ).OrderBy(x => x).ToList(); if (names.Count > 0) { WriteHighlightLine($"# {categoryPair.Key}"); WriteHighlightAligned(" - ", string.Join(", ", names)); WriteHighlightLine(""); } } }