summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2023-05-02 11:10:58 -0400
committerGitHub <noreply@github.com>2023-05-02 11:10:58 -0400
commit29cb65585782f71a9c6fa1062eaa0b8de8359604 (patch)
treecdd820f2c4a8b26933ba4f5057f92e84b8dc01eb /source
parent19c0866b050a022406867aa650302f4efbf8e010 (diff)
Markdown CommandOptions (#2860)
* WIP CommandOptions * Fix some output issues. * Simplify word wrapping. * Add file extensions. * Change how lookup takes place. Add appendSplit functions to StringUtil. Make Categories hold the index range of their options. * Small improvement. * Lookup with partial option names. * Associate user values. * Encoding flags in the name. * Refactor setting up of command options. * Use CommandOptions in slang-options. * Remove old help text. * Cache the CommandOptions on the Session. * Range checking. Fix bug in the Options handling. * Extra checks for validity. * Get categories directly. * Slight improvements over output. * Added NameValue types. * Fix typo. Remove some now unused diagnostics. Fix diagnostic in testing, as output has changed. * Add minimal usage message. * Remove platform executable extension from diagnostics output. * Some improvements around getting names from NameValue types. * Improve some option descriptions. * Small fixes. * WIP improvements around CommandOptions. * Split out CommandOptionsWriter. * Add links to options. * Add command line options reference. * Link to the reference command line information. * Add quick links. * Improvements around lookup. Add categories to linking. * Small additional fixes. * Add LinkFlags control. * Small text fixes. * Fix typo. * Fix typo. * Fix typo. * Add support for -g and -O using CommandOptions. * Improve generated doc output/descriptions. Remove options listed directly in documentation.
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-command-options-writer.cpp712
-rw-r--r--source/core/slang-command-options-writer.h69
-rw-r--r--source/core/slang-command-options.cpp394
-rw-r--r--source/core/slang-command-options.h82
-rw-r--r--source/core/slang-string-util.cpp34
-rw-r--r--source/core/slang-string-util.h5
-rw-r--r--source/core/slang-type-text-util.cpp71
-rw-r--r--source/core/slang-type-text-util.h21
-rw-r--r--source/slang/slang-diagnostic-defs.h2
-rw-r--r--source/slang/slang-options.cpp256
10 files changed, 1193 insertions, 453 deletions
diff --git a/source/core/slang-command-options-writer.cpp b/source/core/slang-command-options-writer.cpp
new file mode 100644
index 000000000..afa46db64
--- /dev/null
+++ b/source/core/slang-command-options-writer.cpp
@@ -0,0 +1,712 @@
+// slang-command-options-writer.cpp
+
+#include "slang-command-options-writer.h"
+
+#include "slang-string-util.h"
+#include "slang-char-util.h"
+#include "slang-byte-encode-util.h"
+
+namespace Slang {
+
+namespace { // anonymous
+typedef CommandOptionsWriter::Style Style;
+} // anonymous
+
+static bool _isMarkdown(Style style) { return style == Style::Markdown || style == Style::NoLinkMarkdown; }
+static bool _hasLinks(Style style) { return style == Style::Markdown; }
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! MarkdownCommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+class MarkdownCommandOptionsWriter : public CommandOptionsWriter
+{
+public:
+ typedef CommandOptionsWriter Super;
+
+ typedef uint32_t LinkFlags;
+ struct LinkFlag
+ {
+ enum Enum
+ {
+ Category = 0x1,
+ Option = 0x2,
+
+ All = Category | Option,
+ };
+ };
+
+ MarkdownCommandOptionsWriter(const Options& options):
+ Super(options)
+ {
+ }
+
+protected:
+ // CommandOptionsWriter
+ virtual void appendDescriptionForCategoryImpl(Index categoryIndex) SLANG_OVERRIDE;
+ virtual void appendDescriptionImpl() SLANG_OVERRIDE;
+
+ void _appendParagraph(const UnownedStringSlice& text, LinkFlags flags = LinkFlag::All);
+ void _appendParagraph(const ConstArrayView<UnownedStringSlice>& words, LinkFlags flags = LinkFlag::All);
+
+ void _appendMaybeLink(const UnownedStringSlice& word, LinkFlags linkFlags);
+
+ void _appendText(const UnownedStringSlice& text);
+ void _appendDescriptionForCategory(Index categoryIndex);
+ UnownedStringSlice _getLinkName(CommandOptions::LookupKind kind, Index index);
+ UnownedStringSlice _getLinkName(const NameKey& key, Index index);
+
+ void _appendQuickLinks();
+
+ bool m_hasLinks = false;
+ Dictionary<NameKey, StringSlicePool::Handle> m_linkMap;
+};
+
+void MarkdownCommandOptionsWriter::appendDescriptionForCategoryImpl(Index categoryIndex)
+{
+ // No point doing links for a single category
+ m_hasLinks = false;
+ _appendDescriptionForCategory(categoryIndex);
+}
+
+
+void MarkdownCommandOptionsWriter::appendDescriptionImpl()
+{
+ m_hasLinks = _hasLinks(m_options.style);
+
+ if (m_hasLinks)
+ {
+ _appendQuickLinks();
+ }
+
+ // Go through categories in order
+ const auto& categories = m_commandOptions->getCategories();
+ for (Index categoryIndex = 0; categoryIndex < categories.getCount(); ++categoryIndex)
+ {
+ _appendDescriptionForCategory(categoryIndex);
+ }
+}
+
+static bool _needsMarkdownEscape(const UnownedStringSlice& text)
+{
+ for (auto c : text)
+ {
+ switch (c)
+ {
+ case '<':
+ case '>':
+ case '&':
+ case '[':
+ case ']':
+ {
+ return true;
+ }
+ default: break;
+ }
+ }
+
+ return false;
+}
+
+void _appendEscapedMarkdown(const UnownedStringSlice& text, StringBuilder& ioBuf)
+{
+ if (_needsMarkdownEscape(text))
+ {
+ // Replace any < > &
+ for (auto c : text)
+ {
+ switch (c)
+ {
+ case '<': ioBuf << "&lt;"; break;
+ case '>': ioBuf << "&gt;"; break;
+ case '&': ioBuf << "&amp;"; break;
+ case '[': ioBuf << "\\["; break;
+ case ']': ioBuf << "\\]"; break;
+ default: ioBuf << c;
+ }
+ }
+ }
+ else
+ {
+ ioBuf << text;
+ }
+}
+
+void MarkdownCommandOptionsWriter::_appendQuickLinks()
+{
+ const auto& categories = m_commandOptions->getCategories();
+ const auto count = categories.getCount();
+
+ m_builder << "## Quick Links\n\n";
+
+ for (Index categoryIndex = 0; categoryIndex < count; ++categoryIndex)
+ {
+ const auto& cat = categories[categoryIndex];
+
+ m_builder << "* [";
+ _appendEscapedMarkdown(cat.name, m_builder);
+ m_builder << "](#" << _getLinkName(LookupKind::Category, categoryIndex) << ")\n";
+ }
+
+ m_builder << "\n";
+}
+
+void MarkdownCommandOptionsWriter::_appendParagraph(const UnownedStringSlice& text, LinkFlags linkFlags)
+{
+ List<UnownedStringSlice> words;
+ StringUtil::splitOnWhitespace(text, words);
+ _appendParagraph(words.getArrayView(), linkFlags);
+}
+
+static bool _isEndPunctionation(char c)
+{
+ return c == '.' || c == ')' || c == ',';
+}
+
+static bool _isStartPunctionation(char c)
+{
+ return c == '(' || c == ',';
+}
+
+static UnownedStringSlice _trimPunctuation(const UnownedStringSlice& word)
+{
+ const char* start = word.begin();
+ const char* end = word.end();
+
+ while (start < end && _isStartPunctionation(*start)) start++;
+ while (end > start && _isEndPunctionation(end[-1])) --end;
+ return UnownedStringSlice(start, end);
+}
+
+void MarkdownCommandOptionsWriter::_appendMaybeLink(const UnownedStringSlice& inWord, LinkFlags linkFlags)
+{
+ if (linkFlags)
+ {
+ auto trimmedWord = _trimPunctuation(inWord);
+
+ if (trimmedWord.getLength())
+ {
+ Index index = -1;
+ NameKey nameKey;
+
+ // Look for options
+ if (trimmedWord[0] == '-' && (linkFlags & LinkFlag::Option))
+ {
+ index = m_commandOptions->findTargetIndexByName(LookupKind::Option, trimmedWord, &nameKey);
+ }
+ else if (trimmedWord[0] == '<' && trimmedWord[trimmedWord.getLength() - 1] == '>' && (linkFlags & LinkFlag::Category))
+ {
+ index = m_commandOptions->findTargetIndexByName(LookupKind::Category, trimmedWord.subString(1, trimmedWord.getLength() - 2), &nameKey);
+ }
+
+ if (index > 0)
+ {
+ // Append before the link
+ _appendEscapedMarkdown(UnownedStringSlice(inWord.begin(), trimmedWord.begin()), m_builder);
+
+ // Make into a link
+ m_builder << "[";
+ _appendEscapedMarkdown(trimmedWord, m_builder);
+ m_builder << "](#" << _getLinkName(nameKey, index) << ")";
+
+ // Append after the link
+ _appendEscapedMarkdown(UnownedStringSlice(trimmedWord.end(), inWord.end()), m_builder);
+ return;
+ }
+ }
+ }
+
+ _appendEscapedMarkdown(inWord, m_builder);
+}
+
+void MarkdownCommandOptionsWriter::_appendParagraph(const ConstArrayView<UnownedStringSlice>& words, LinkFlags linkFlags)
+{
+ if (m_hasLinks && linkFlags)
+ {
+ for (auto word : words)
+ {
+ _appendMaybeLink(word, linkFlags);
+ m_builder << " ";
+ }
+ }
+ else
+ {
+ for (auto word : words)
+ {
+ _appendEscapedMarkdown(word, m_builder);
+ m_builder << " ";
+ }
+ }
+}
+
+void MarkdownCommandOptionsWriter::_appendText(const UnownedStringSlice& text)
+{
+ List<UnownedStringSlice> lines;
+ StringUtil::calcLines(text, lines);
+ for (auto line : lines)
+ {
+ if (line.startsWith(toSlice(" ")))
+ {
+ // If prefixed means we want to display as is
+ m_builder << "> " << line << "\n";
+ }
+ else
+ {
+ _appendParagraph(line);
+ m_builder << "\n\n";
+ }
+ }
+}
+
+
+void MarkdownCommandOptionsWriter::_appendDescriptionForCategory(Index categoryIndex)
+{
+ auto& options = *m_commandOptions;
+
+ const auto& categories = options.getCategories();
+ const auto& category = categories[categoryIndex];
+
+ const bool isValue = (category.kind == CommandOptions::CategoryKind::Value);
+
+ // Header
+ {
+ if (m_hasLinks)
+ {
+ // Output anchor
+ m_builder << "<a id=\"" << _getLinkName(LookupKind::Category, categoryIndex) << "\"></a>\n";
+ }
+
+ m_builder << "# " << category.name << "\n\n";
+
+ // If there is a description output, making \n split paragraphs
+ if (category.description.getLength() > 0)
+ {
+ _appendText(category.description);
+ }
+ }
+
+ for (Index optionIndex = category.optionStartIndex; optionIndex < category.optionEndIndex; ++optionIndex)
+ {
+ const auto& option = options.getOptionAt(optionIndex);
+
+ {
+ List<UnownedStringSlice> names;
+ StringUtil::split(option.names, ',', names);
+
+ if (isValue)
+ {
+ m_builder << "* ";
+ // Output all the names
+ m_builder << "`";
+ StringUtil::join(names.getBuffer(), names.getCount(), toSlice("`, `"), m_builder);
+ m_builder << "` ";
+ }
+ else
+ {
+ if (m_hasLinks)
+ {
+ m_builder << "<a id=\"" << _getLinkName(LookupKind::Option, optionIndex) << "\"></a>\n";
+ }
+
+ m_builder << "## ";
+ StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), m_builder);
+ m_builder << "\n";
+
+ if (option.usage.getLength())
+ {
+ m_builder << "\n**";
+
+ if (m_hasLinks)
+ {
+ List<UnownedStringSlice> usedCategories;
+ options.splitUsage(option.usage, usedCategories);
+
+ const char* cur = option.usage.begin();
+ for (auto usedCategory : usedCategories)
+ {
+ _appendEscapedMarkdown(UnownedStringSlice(cur, usedCategory.begin()), m_builder);
+
+ // Now do the link
+ const Index usedCategoryIndex = options.findCategoryByName(usedCategory);
+
+ m_builder << "[" << usedCategory << "](#" << _getLinkName(LookupKind::Category, usedCategoryIndex) << ")";
+
+ cur = usedCategory.end();
+ }
+
+ _appendEscapedMarkdown(UnownedStringSlice(cur, option.usage.end()), m_builder);
+ }
+ else
+ {
+ _appendEscapedMarkdown(option.usage, m_builder);
+ }
+
+ m_builder << "**\n\n";
+ }
+ }
+ }
+
+ if (option.description.getLength() > 0)
+ {
+ if (isValue)
+ {
+ m_builder << ": ";
+ _appendParagraph(option.description);
+ }
+ else
+ {
+ _appendText(option.description);
+ }
+ }
+
+ m_builder << "\n";
+ }
+
+ m_builder << "\n";
+}
+
+UnownedStringSlice MarkdownCommandOptionsWriter::_getLinkName(const NameKey& key, Index index)
+{
+ if (auto ptr = m_linkMap.tryGetValue(key))
+ {
+ return m_pool.getSlice(*ptr);
+ }
+
+ UnownedStringSlice prefix = (key.kind == CommandOptions::LookupKind::Category) ?
+ m_commandOptions->getFirstNameForCategory(index) :
+ m_commandOptions->getFirstNameForOption(index);
+ prefix = prefix.trim('-');
+
+ if (prefix.getLength() == 0)
+ {
+ prefix = toSlice("id");
+ }
+
+ StringBuilder buf;
+ buf << prefix;
+
+ const auto bufLen = buf.getLength();
+
+ for (Index i = 0; i < 1000; ++i)
+ {
+ buf.reduceLength(bufLen);
+
+ if (i > 0)
+ {
+ buf << "-" << i;
+ }
+
+ if (!m_pool.has(buf.getUnownedSlice()))
+ {
+ break;
+ }
+ }
+
+ const auto handle = m_pool.add(buf.getUnownedSlice());
+ m_linkMap.add(key, handle);
+
+ return m_pool.getSlice(handle);
+}
+
+UnownedStringSlice MarkdownCommandOptionsWriter::_getLinkName(CommandOptions::LookupKind kind, Index index)
+{
+ auto& options = *m_commandOptions;
+
+ // Set up the name key
+ const auto key = (kind == LookupKind::Category) ?
+ options.getNameKeyForCategory(index) :
+ options.getNameKeyForOption(index);
+
+ return _getLinkName(key, index);
+
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TextCommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+class TextCommandOptionsWriter : public CommandOptionsWriter
+{
+public:
+ typedef CommandOptionsWriter Super;
+
+ TextCommandOptionsWriter(const Options& options) :
+ Super(options)
+ {
+ }
+protected:
+ // CommandOptionsWriter
+ virtual void appendDescriptionForCategoryImpl(Index categoryIndex) SLANG_OVERRIDE;
+ virtual void appendDescriptionImpl() SLANG_OVERRIDE;
+
+ void _appendText(Count indentCount, const UnownedStringSlice& text);
+ void _appendDescriptionForCategory(Index categoryIndex);
+};
+
+void TextCommandOptionsWriter::appendDescriptionForCategoryImpl(Index categoryIndex)
+{
+ _appendDescriptionForCategory(categoryIndex);
+}
+
+void TextCommandOptionsWriter::appendDescriptionImpl()
+{
+ const auto& categories = m_commandOptions->getCategories();
+ for (Index categoryIndex = 0; categoryIndex < categories.getCount(); ++categoryIndex)
+ {
+ _appendDescriptionForCategory(categoryIndex);
+ }
+}
+
+void TextCommandOptionsWriter::_appendDescriptionForCategory(Index categoryIndex)
+{
+ auto& options = *m_commandOptions;
+
+ const auto& categories = options.getCategories();
+ const auto& category = categories[categoryIndex];
+
+ // Header
+ {
+ const auto count = m_builder.getLength();
+ if (category.kind == CategoryKind::Value)
+ {
+ m_builder << "<" << category.name << ">";
+ }
+ else
+ {
+ m_builder << category.name;
+ }
+
+ const auto length = m_builder.getLength() - count;
+ m_builder << "\n";
+
+ m_builder.appendRepeatedChar('=', length);
+
+ m_builder << "\n\n";
+
+ // If there is a description output it
+ if (category.description.getLength() > 0)
+ {
+ _appendText(0, category.description);
+ m_builder << "\n";
+ }
+ }
+
+ for (auto& option : options.getOptionsForCategory(categoryIndex))
+ {
+ m_builder << m_options.indent;
+
+ if (option.usage.getLength())
+ {
+ m_builder << option.usage;
+ }
+ else
+ {
+ List<UnownedStringSlice> names;
+ StringUtil::split(option.names, ',', names);
+
+ _appendWrappedIndented(1, names, toSlice(", "));
+ }
+
+ if (option.description.getLength() == 0)
+ {
+ m_builder << "\n";
+ continue;
+ }
+
+ m_builder << ": ";
+
+ _appendText(2, option.description);
+
+ if (option.usage.getLength())
+ {
+ List<Index> usageCategoryIndices;
+ options.findCategoryIndicesFromUsage(option.usage, usageCategoryIndices);
+
+ for (auto usageCategoryIndex : usageCategoryIndices)
+ {
+ auto& usageCat = categories[usageCategoryIndex];
+
+ m_builder << m_options.indent << m_options.indent;
+
+ m_builder << "<" << usageCat.name << "> can be: ";
+
+ List<UnownedStringSlice> optionNames;
+ options.getCategoryOptionNames(usageCategoryIndex, optionNames);
+
+ _appendWrappedIndented(2, optionNames, toSlice(", "));
+
+ m_builder << "\n";
+ }
+ }
+ }
+
+ m_builder << "\n";
+}
+
+void TextCommandOptionsWriter::_appendText(Count indentCount, const UnownedStringSlice& text)
+{
+ List<UnownedStringSlice> lines;
+ StringUtil::calcLines(text, lines);
+
+ // Remove very last line if it's empty
+ if (lines.getCount() > 1 && lines.getLast().trim().getLength() == 0)
+ {
+ lines.removeLast();
+ }
+
+ List<UnownedStringSlice> words;
+
+ for (auto line : lines)
+ {
+ if (line.startsWith(toSlice(" ")))
+ {
+ // Append the line as is after the indent
+ _requireIndent(indentCount);
+ m_builder << line;
+ }
+ else if (line.trim().getLength() == 0)
+ {
+ }
+ else
+ {
+ words.clear();
+ StringUtil::split(line, ' ', words);
+
+ _requireIndent(indentCount);
+ _appendWrappedIndented(indentCount, words, toSlice(" "));
+ }
+
+ m_builder << "\n";
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+typedef CommandOptionsWriter::Style Style;
+
+static const NamesDescriptionValue s_styleInfos[] =
+{
+ { ValueInt(Style::Text), "text", "Text suitable for output to a terminal" },
+ { ValueInt(Style::Markdown), "markdown", "Markdown" },
+ { ValueInt(Style::NoLinkMarkdown), "no-link-markdown", "Markdown without links" },
+};
+
+/* static */ConstArrayView<NamesDescriptionValue> CommandOptionsWriter::getStyleInfos()
+{
+ return makeConstArrayView(s_styleInfos);
+}
+
+CommandOptionsWriter::CommandOptionsWriter(const Options& options) :
+ m_pool(StringSlicePool::Style::Default),
+ m_options(options)
+{
+ m_options.indent = m_pool.addAndGetSlice(options.indent);
+}
+
+/* static */RefPtr<CommandOptionsWriter> CommandOptionsWriter::create(const Options& options)
+{
+ if (_isMarkdown(options.style))
+ {
+ return new MarkdownCommandOptionsWriter(options);
+ }
+ else
+ {
+ return new TextCommandOptionsWriter(options);
+ }
+}
+
+void CommandOptionsWriter::appendDescriptionForCategory(CommandOptions* options, Index categoryIndex)
+{
+ m_commandOptions = options;
+ appendDescriptionForCategoryImpl(categoryIndex);
+ m_commandOptions = nullptr;
+}
+
+void CommandOptionsWriter::appendDescription(CommandOptions* options)
+{
+ m_commandOptions = options;
+ appendDescriptionImpl();
+ m_commandOptions = nullptr;
+}
+
+Count CommandOptionsWriter::_getCurrentLineLength()
+{
+ // Work out the current line length
+ const char* start = m_builder.begin();
+ const char* cur = m_builder.end();
+
+ Count lineLength = 0;
+
+ if (cur > start)
+ {
+ for (--cur; cur > start; --cur)
+ {
+ const auto c = *cur;
+ if (c == '\n' || c == '\r')
+ {
+ ++cur;
+ break;
+ }
+ }
+
+ lineLength = Count(ptrdiff_t(m_builder.end() - cur));
+ }
+
+ return lineLength;
+}
+
+void CommandOptionsWriter::_requireIndent(Count indentCount)
+{
+ const auto length = m_builder.getLength();
+ if (length)
+ {
+ const auto c = m_builder[length - 1];
+ if (c == '\n' || c == '\r')
+ {
+ for (Index j = 0; j < indentCount; j++)
+ {
+ m_builder.append(m_options.indent);
+ }
+ }
+ }
+}
+
+void CommandOptionsWriter::_appendWrappedIndented(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit)
+{
+ Count lineLength = _getCurrentLineLength();
+
+ const auto count = slices.getCount();
+
+ for (Index i = 0; i < count; ++i)
+ {
+ auto slice = slices[i];
+
+ auto sliceLength = slice.getLength();
+
+ if (i < count - 1)
+ {
+ sliceLength += delimit.getLength();
+ }
+
+ // If out of space onto the next line
+ if (lineLength + sliceLength > m_options.lineLength)
+ {
+ m_builder.append("\n");
+
+ lineLength = indentCount * m_options.indent.getLength();
+
+ for (Index j = 0; j < indentCount; j++)
+ {
+ m_builder.append(m_options.indent);
+ }
+ }
+
+ m_builder.append(slice);
+ if (i < count - 1)
+ {
+ m_builder.append(delimit);
+ }
+
+ lineLength += sliceLength;
+ }
+}
+
+} // namespace Slang
+
+
diff --git a/source/core/slang-command-options-writer.h b/source/core/slang-command-options-writer.h
new file mode 100644
index 000000000..eb9a2795f
--- /dev/null
+++ b/source/core/slang-command-options-writer.h
@@ -0,0 +1,69 @@
+#ifndef SLANG_CORE_COMMAND_OPTIONS_WRITER_H
+#define SLANG_CORE_COMMAND_OPTIONS_WRITER_H
+
+#include "slang-command-options.h"
+
+namespace Slang
+{
+
+class CommandOptionsWriter : public RefObject
+{
+public:
+ typedef CommandOptions::CategoryKind CategoryKind;
+ typedef CommandOptions::NameKey NameKey;
+ typedef CommandOptions::LookupKind LookupKind;
+
+ enum class Style
+ {
+ Text, ///< Suitable for output to a terminal
+ Markdown, ///< Markdown
+ NoLinkMarkdown, ///< Markdown without links
+ };
+
+ static ConstArrayView<NamesDescriptionValue> getStyleInfos();
+
+ struct Options
+ {
+ Style style = Style::Text; ///< The style
+ Index lineLength = 120; ///< The maximum amount of characters on a line
+ UnownedStringSlice indent = toSlice(" ");;
+ };
+
+ /// Append descirption for a category
+ void appendDescriptionForCategory(CommandOptions* options, Index categoryIndex);
+ /// Appends a description of all of the options
+ void appendDescription(CommandOptions* options);
+
+ /// Get the builder that string is being written to
+ StringBuilder& getBuilder() { return m_builder; }
+
+ static RefPtr<CommandOptionsWriter> create(const Options& options);
+
+
+protected:
+
+ /// Append descirption for a category
+ virtual void appendDescriptionForCategoryImpl(Index categoryIndex) = 0;
+ /// Appends a description of all of the options
+ virtual void appendDescriptionImpl() = 0;
+
+ // Ctor, use create to create a writer
+ CommandOptionsWriter(const Options& options);
+
+ /// Get the length of the current line in ascii chars/bytes
+ Count _getCurrentLineLength();
+
+ /// Indentation/wrapping
+ void _requireIndent(Count indentCount);
+ void _appendWrappedIndented(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit);
+
+ CommandOptions* m_commandOptions = nullptr;
+
+ StringSlicePool m_pool;
+ StringBuilder m_builder;
+ Options m_options;
+};
+
+} // namespace Slang
+
+#endif
diff --git a/source/core/slang-command-options.cpp b/source/core/slang-command-options.cpp
index 4577bcb8e..5bbe59a0d 100644
--- a/source/core/slang-command-options.cpp
+++ b/source/core/slang-command-options.cpp
@@ -7,7 +7,37 @@
#include "slang-byte-encode-util.h"
namespace Slang {
-
+
+UnownedStringSlice CommandOptions::getFirstNameForOption(Index optionIndex)
+{
+ const auto& opt = m_options[optionIndex];
+ return StringUtil::getAtInSplit(opt.names, ',', 0);
+}
+
+UnownedStringSlice CommandOptions::getFirstNameForCategory(Index categoryIndex)
+{
+ const auto& cat = m_categories[categoryIndex];
+ return cat.name;
+}
+
+CommandOptions::NameKey CommandOptions::getNameKeyForOption(Index optionIndex)
+{
+ const auto& opt = m_options[optionIndex];
+ const auto& cat = m_categories[opt.categoryIndex];
+ NameKey key;
+ key.nameIndex = m_pool.findIndex(getFirstNameForOption(optionIndex));
+ key.kind = (cat.kind == CategoryKind::Option) ? LookupKind::Option : makeLookupKind(opt.categoryIndex);
+ return key;
+}
+
+CommandOptions::NameKey CommandOptions::getNameKeyForCategory(Index categoryIndex)
+{
+ NameKey key;
+ key.nameIndex = m_pool.findIndex(getFirstNameForCategory(categoryIndex));
+ key.kind = LookupKind::Category;
+ return key;
+}
+
SlangResult CommandOptions::_addName(LookupKind kind, const UnownedStringSlice& name, Index targetIndex)
{
NameKey nameKey;
@@ -80,7 +110,16 @@ UnownedStringSlice CommandOptions::_addString(const UnownedStringSlice& slice)
Index CommandOptions::_addOption(const UnownedStringSlice& name, const Option& inOption)
{
- return _addOption(&name, 1, inOption);
+ if (name.indexOf(',') < 0)
+ {
+ return _addOption(&name, 1, inOption);
+ }
+ else
+ {
+ List<UnownedStringSlice> names;
+ StringUtil::split(name, ',', names);
+ return _addOption(names.getBuffer(), names.getCount(), inOption);
+ }
}
Index CommandOptions::_addOption(const UnownedStringSlice* names, Count namesCount, const Option& inOption)
@@ -386,7 +425,52 @@ void CommandOptions::setCategory(const char* name)
m_currentCategoryIndex = -1;
}
-Index CommandOptions::findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name) const
+Index CommandOptions::findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name, NameKey* outNameKey) const
+{
+ // Look up directly
+ {
+ auto index = _findTargetIndexByName(kind, name, outNameKey);
+ if (index >= 0)
+ {
+ return index;
+ }
+ }
+
+ // Special case options, which can have prefix styles
+ if (kind == LookupKind::Option)
+ {
+ auto prefixSizes = m_prefixSizes;
+
+ while (prefixSizes)
+ {
+ auto prefixSize = ByteEncodeUtil::calcMsb32(prefixSizes);
+
+ if (prefixSize < name.getLength())
+ {
+ // Look it up
+ const auto index = _findTargetIndexByName(kind, name.head(prefixSize), outNameKey);
+ if (index >= 0)
+ {
+ auto& option = m_options[index];
+
+ // If the option accepts prefixes, we return the index
+ if (option.flags & (Flag::CanPrefix | Flag::IsPrefix))
+ {
+ return index;
+ }
+ }
+ }
+
+ // Remove the bit
+ prefixSizes &= ~(uint32_t(1) << prefixSize);
+ }
+ }
+
+ // Was not found
+ return -1;
+}
+
+Index CommandOptions::_findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name, NameKey* outNameKey) const
{
const auto nameIndex = m_pool.findIndex(name);
// If the name isn't in the pool then there isn't a category with this name
@@ -401,6 +485,10 @@ Index CommandOptions::findTargetIndexByName(LookupKind kind, const UnownedString
if (auto ptr = m_nameMap.tryGetValue(key))
{
+ if (outNameKey)
+ {
+ *outNameKey = key;
+ }
return *ptr;
}
@@ -447,66 +535,31 @@ Index CommandOptions::findOptionByCategoryUserValue(UserValue categoryUserValue,
return findValueByName(categoryIndex, name);
}
-Index CommandOptions::findOptionByName(const UnownedStringSlice& name) const
-{
- {
- auto index = findTargetIndexByName(LookupKind::Option, name);
- if (index >= 0)
- {
- return index;
- }
- }
-
- // We need to search for partials
- auto prefixSizes = m_prefixSizes;
-
- while (prefixSizes)
- {
- auto prefixSize = ByteEncodeUtil::calcMsb32(prefixSizes);
-
- if (prefixSize < name.getLength())
- {
- // Look it up
- const auto index = findTargetIndexByName(LookupKind::Option, name.head(prefixSize));
- if (index >= 0)
- {
- auto& option = m_options[index];
-
- // If the option accepts prefixes, we return the index
- if (option.flags & (Flag::CanPrefix | Flag::IsPrefix))
- {
- return index;
- }
- }
- }
-
- // Remove the bit
- prefixSizes &= ~(uint32_t(1) << prefixSize);
- }
-
- // Was not found
- return -1;
-}
-
ConstArrayView<CommandOptions::Option> CommandOptions::getOptionsForCategory(Index categoryIndex) const
{
const auto& cat = m_categories[categoryIndex];
return makeConstArrayView(m_options.getBuffer() + cat.optionStartIndex, cat.optionEndIndex - cat.optionStartIndex);
}
-void CommandOptions::getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const
+
+void CommandOptions::appendCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const
{
- outNames.clear();
for (const auto& option : getOptionsForCategory(categoryIndex))
{
StringUtil::appendSplit(option.names, ',', outNames);
}
}
-void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slice, List<Index>& outCategories) const
+void CommandOptions::getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const
+{
+ outNames.clear();
+ appendCategoryOptionNames(categoryIndex, outNames);
+}
+
+void CommandOptions::splitUsage(const UnownedStringSlice& usageSlice, List<UnownedStringSlice>& outSlices) const
{
- const auto* cur = slice.begin();
- const auto* end = slice.end();
+ const auto* cur = usageSlice.begin();
+ const auto* end = usageSlice.end();
while (cur < end)
{
@@ -522,16 +575,16 @@ void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slic
{
cur++;
}
-
+
// If we hit closing > we want to lookup
if (cur < end && *cur == '>')
{
const UnownedStringSlice categoryName(start, cur);
Index categoryIndex = findCategoryByName(categoryName);
- if (categoryIndex >= 0 && outCategories.indexOf(categoryIndex) < 0)
+ if (categoryIndex >= 0)
{
- outCategories.add(categoryIndex);
+ outSlices.add(categoryName);
}
}
@@ -540,6 +593,22 @@ void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slic
}
}
+
+void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slice, List<Index>& outCategories) const
+{
+ List<UnownedStringSlice> categoryNames;
+ splitUsage(slice, categoryNames);
+
+ for (auto name : categoryNames)
+ {
+ Index categoryIndex = findCategoryByName(name);
+ if (categoryIndex >= 0 && outCategories.indexOf(categoryIndex) < 0)
+ {
+ outCategories.add(categoryIndex);
+ }
+ }
+}
+
Count CommandOptions::getOptionCountInRange(Index categoryIndex, UserValue start, UserValue nonInclEnd) const
{
const UserIndex startIndex = UserIndex(start);
@@ -616,227 +685,6 @@ bool CommandOptions::hasContiguousUserValueRange(LookupKind kind, UserValue star
return rangeCount == count;
}
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!! CommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */
-
-void CommandOptionsWriter::appendDescriptionForCategory(const CommandOptions& options, Index categoryIndex)
-{
- const auto& categories = options.getCategories();
-
- const auto& category = categories[categoryIndex];
-
- // Header
- {
- const auto count = m_builder.getLength();
- if (category.kind == CategoryKind::Value)
- {
- m_builder << "<" << category.name << ">";
- }
- else
- {
- m_builder << category.name;
- }
-
- const auto length = m_builder.getLength() - count;
- m_builder << "\n";
-
- m_builder.appendRepeatedChar('=', length);
-
- m_builder << "\n\n";
-
- // If there is a description output it
- if (category.description.getLength() > 0)
- {
- _appendText(0, category.description);
- m_builder << "\n";
- }
- }
-
- for (auto& option : options.getOptionsForCategory(categoryIndex))
- {
- m_builder << m_indentSlice;
-
- if (option.usage.getLength())
- {
- m_builder << option.usage;
- }
- else
- {
- List<UnownedStringSlice> names;
- StringUtil::split(option.names, ',', names);
-
- _appendWithWrap(1, names, toSlice(", "));
- }
-
- if (option.description.getLength() == 0)
- {
- m_builder << "\n";
- continue;
- }
-
- m_builder << ": ";
-
- _appendText(2, option.description);
-
- if (option.usage.getLength())
- {
- List<Index> usageCategoryIndices;
- options.findCategoryIndicesFromUsage(option.usage, usageCategoryIndices);
-
- for (auto usageCategoryIndex : usageCategoryIndices)
- {
- auto& usageCat = categories[usageCategoryIndex];
-
- m_builder << m_indentSlice << m_indentSlice;
- m_builder << "<" << usageCat.name << "> can be: ";
-
- List<UnownedStringSlice> optionNames;
- options.getCategoryOptionNames(usageCategoryIndex, optionNames);
-
- _appendWithWrap(2, optionNames, toSlice(", "));
-
- m_builder << "\n";
- }
- }
- }
-
- m_builder << "\n";
-}
-
-void CommandOptionsWriter::appendDescription(const CommandOptions& options)
-{
- // Go through categories in order
-
- const auto& categories = options.getCategories();
-
- for (Index categoryIndex = 0; categoryIndex < categories.getCount(); ++categoryIndex)
- {
- appendDescriptionForCategory(options, categoryIndex);
- }
-}
-
-void CommandOptionsWriter::_appendText(Count indentCount, const UnownedStringSlice& text)
-{
- List<UnownedStringSlice> lines;
- StringUtil::calcLines(text, lines);
-
- // Remove very last line if it's empty
- if (lines.getCount() > 1 && lines.getLast().trim().getLength() == 0)
- {
- lines.removeLast();
- }
-
- _appendWithWrap(indentCount, lines);
-}
-
-Count CommandOptionsWriter::_getCurrentLineLength()
-{
- // Work out the current line length
- const char* start = m_builder.begin();
- const char* cur = m_builder.end();
-
- Count lineLength = 0;
-
- if (cur > start)
- {
- for (--cur; cur > start; --cur)
- {
- const auto c = *cur;
- if (c == '\n' || c == '\r')
- {
- ++cur;
- break;
- }
- }
-
- lineLength = Count(ptrdiff_t(m_builder.end() - cur));
- }
-
- return lineLength;
-}
-
-void CommandOptionsWriter::_requireIndent(Count indentCount)
-{
- const auto length = m_builder.getLength();
- if (length)
- {
- const auto c = m_builder[length - 1];
- if (c == '\n' || c == '\r')
- {
- for (Index j = 0; j < indentCount; j++)
- {
- m_builder.append(m_indentSlice);
- }
- }
- }
-}
-
-void CommandOptionsWriter::_appendWithWrap(Count indentCount, List<UnownedStringSlice>& lines)
-{
- List<UnownedStringSlice> words;
-
- for (auto line : lines)
- {
- if (line.trim().getLength() == 0 || line.startsWith(toSlice(" ")))
- {
- // Append the line as is after the indent
- _requireIndent(indentCount);
- m_builder << line << "\n";
- }
- else
- {
- words.clear();
- StringUtil::split(line, ' ', words);
-
- _requireIndent(indentCount);
-
- _appendWithWrap(indentCount, words, toSlice(" "));
- m_builder << "\n";
- }
- }
-}
-
-
-
-void CommandOptionsWriter::_appendWithWrap(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit)
-{
- Count lineLength = _getCurrentLineLength();
-
- const auto count = slices.getCount();
-
- for (Index i = 0; i < count; ++i)
- {
- auto slice = slices[i];
-
- auto sliceLength = slice.getLength();
-
- if (i < count - 1)
- {
- sliceLength += delimit.getLength();
- }
-
- // If out of space onto the next line
- if (lineLength + sliceLength > m_lineLength)
- {
- m_builder.append("\n");
-
- lineLength = indentCount * m_indentSlice.getLength();
-
- for (Index j = 0; j < indentCount; j++)
- {
- m_builder.append(m_indentSlice);
- }
- }
-
- m_builder.append(slice);
- if (i < count - 1)
- {
- m_builder.append(delimit);
- }
-
- lineLength += sliceLength;
- }
-}
-
} // namespace Slang
diff --git a/source/core/slang-command-options.h b/source/core/slang-command-options.h
index a92d4d8f8..8b6d7b0ce 100644
--- a/source/core/slang-command-options.h
+++ b/source/core/slang-command-options.h
@@ -28,6 +28,20 @@ struct CommandOptions
Base = 0, ///< Lookup via category index
};
+ /// A key type that uses the combination of the lookup kind and a name index.
+ /// Maps to a target index that could be a category or an option index.
+ struct NameKey
+ {
+ typedef NameKey ThisType;
+
+ SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return kind == rhs.kind && nameIndex == rhs.nameIndex; }
+ SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+ HashCode getHashCode() const { return combineHash(Slang::getHashCode(kind), Slang::getHashCode(nameIndex)); }
+
+ LookupKind kind; ///< The kind of lookup
+ Index nameIndex; ///< The name index in the pool
+ };
+
enum class CategoryKind
{
Option, ///< Command line option (like "-D")
@@ -74,6 +88,16 @@ struct CommandOptions
Flags flags = 0; ///< Flags about this option
};
+ /// Get the first name
+ UnownedStringSlice getFirstNameForOption(Index optionIndex);
+ /// Get the first name for the category
+ UnownedStringSlice getFirstNameForCategory(Index categoryIndex);
+
+ /// Get a name key for an opton
+ NameKey getNameKeyForOption(Index optionIndex);
+ /// Get a name key for a category
+ NameKey getNameKeyForCategory(Index optionIndex);
+
/// Add a category
Index addCategory(CategoryKind kind, const char* name, const char* description, UserValue userValue = kInvalidUserValue);
/// Use an already known category. It's an error if the category isn't found
@@ -100,14 +124,14 @@ struct CommandOptions
void addValuesWithAliases(const ConstArrayView<NameValue>& values);
/// Get the target index based off the name and the kind
- Index findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name) const;
+ Index findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name, NameKey* outNameKey = nullptr) const;
/// Given a kind and a user value lookup the target index
Index findTargetIndexByUserValue(LookupKind kind, UserValue userValue) const;
/// Finds the category by name or -1 if not found
Index findCategoryByName(const UnownedStringSlice& name) const { return findTargetIndexByName(LookupKind::Category, name); }
/// Finds the option index by name or -1 if not found
- Index findOptionByName(const UnownedStringSlice& name) const;
+ Index findOptionByName(const UnownedStringSlice& name) const { return findTargetIndexByName(LookupKind::Option, name); }
/// Find the option index of a value, using it's category index and the name
Index findValueByName(Index categoryIndex, const UnownedStringSlice& name) const { return findTargetIndexByName(LookupKind(categoryIndex), name); }
@@ -139,9 +163,14 @@ struct CommandOptions
/// Find all of the categories in the usage slice
void findCategoryIndicesFromUsage(const UnownedStringSlice& usageSlice, List<Index>& outCategories) const;
+
+ /// Splits usage into category slices
+ void splitUsage(const UnownedStringSlice& usageSlice, List<UnownedStringSlice>& outSlices) const;
+
/// Get all the option names associated with a category index
void getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const;
-
+ void appendCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const;
+
/// Set up a lookup kind from a category index
static LookupKind makeLookupKind(Index categoryIndex) { return LookupKind(categoryIndex); }
@@ -160,6 +189,7 @@ struct CommandOptions
{
}
+ protected:
/// Returns name in the m_optionPool or -1 on error
SlangResult _addOptionName(const UnownedStringSlice& name, Flags flags, Index targetIndex);
SlangResult _addValueName(const UnownedStringSlice& name, Index categoryIndex, Index targetIndex);
@@ -175,19 +205,7 @@ struct CommandOptions
UnownedStringSlice _addString(const char* text);
UnownedStringSlice _addString(const UnownedStringSlice& slice);
- /// A key type that uses the combination of the lookup kind and a name index.
- /// Maps to a target index that could be a category or an option index.
- struct NameKey
- {
- typedef NameKey ThisType;
-
- SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return kind == rhs.kind && nameIndex == rhs.nameIndex; }
- SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
- HashCode getHashCode() const { return combineHash(Slang::getHashCode(kind), Slang::getHashCode(nameIndex)); }
-
- LookupKind kind; ///< The kind of lookup
- Index nameIndex; ///< The name index in the pool
- };
+ Index _findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name, NameKey* outNameKey) const;
struct UserValueKey
{
@@ -216,38 +234,6 @@ struct CommandOptions
MemoryArena m_arena; ///< For other misc storage
};
-struct CommandOptionsWriter
-{
- typedef CommandOptions::CategoryKind CategoryKind;
-
- /// Append descirption for a category
- void appendDescriptionForCategory(const CommandOptions& options, Index categoryIndex);
- /// Appends a description of all of the options
- void appendDescription(const CommandOptions& options);
-
- /// Get the builder that string is being written to
- StringBuilder& getBuilder() { return m_builder; }
-
- /// Ctor
- CommandOptionsWriter():
- m_indentSlice(toSlice(" "))
- {
- }
-
- Count _getCurrentLineLength();
-
- void _appendWithWrap(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit);
- void _appendWithWrap(Count indentCount, List<UnownedStringSlice>& lines);
- void _requireIndent(Count indentCount);
- void _appendText(Count indentCount, const UnownedStringSlice& text);
-
-
- UnownedStringSlice m_indentSlice;
- Count m_lineLength = 80;
-
- StringBuilder m_builder;
-};
-
} // namespace Slang
#endif
diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp
index 7de6846d5..ac8a176ad 100644
--- a/source/core/slang-string-util.cpp
+++ b/source/core/slang-string-util.cpp
@@ -35,6 +35,34 @@ namespace Slang {
return areAllEqual(slicesA, slicesB, equalFn);
}
+/* static */void StringUtil::appendSplitOnWhitespace(const UnownedStringSlice& in, List<UnownedStringSlice>& outSlices)
+{
+ const char* start = in.begin();
+ const char* end = in.end();
+
+ // Skip any at the start
+ while (start < end && CharUtil::isWhitespace(*start)) start++;
+
+ while (start < end)
+ {
+ // Find all the non white space in a run
+ const char* cur = start;
+ while (cur < end && !CharUtil::isWhitespace(*cur))
+ {
+ cur++;
+ }
+
+ // Add to output
+ outSlices.add(UnownedStringSlice(start, cur));
+
+ // Find the next start
+ start = cur + 1;
+
+ // Skip the split
+ while (start < end && CharUtil::isWhitespace(*start)) start++;
+ }
+}
+
/* static */void StringUtil::appendSplit(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices)
{
const char* start = in.begin();
@@ -113,6 +141,12 @@ namespace Slang {
appendSplit(in, splitSlice, outSlices);
}
+/* static */void StringUtil::splitOnWhitespace(const UnownedStringSlice& in, List<UnownedStringSlice>& outSlices)
+{
+ outSlices.clear();
+ appendSplitOnWhitespace(in, outSlices);
+}
+
/* static */Index StringUtil::split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices)
{
Index index = 0;
diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h
index 2293acf8d..4b9a4d5d1 100644
--- a/source/core/slang-string-util.h
+++ b/source/core/slang-string-util.h
@@ -32,6 +32,8 @@ struct StringUtil
static Index split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices);
/// Splits into outSlices up to maxSlices. Returns SLANG_OK if of 'in' consumed.
static SlangResult split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices, Index& outSlicesCount);
+ /// Splits on white space
+ static void splitOnWhitespace(const UnownedStringSlice& in, List<UnownedStringSlice>& slicesOut);
/// Split in, by specified splitChar append into slices out
/// Slices contents will directly address into in, so contents will only stay valid as long as in does.
@@ -40,6 +42,9 @@ struct StringUtil
/// Slices contents will directly address into in, so contents will only stay valid as long as in does.
static void appendSplit(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& slicesOut);
+ /// appends splits on white space
+ static void appendSplitOnWhitespace(const UnownedStringSlice& in, List<UnownedStringSlice>& slicesOut);
+
/// Append the joining of in items, separated by 'separator' onto out
static void join(const List<String>& in, char separator, StringBuilder& out);
static void join(const List<String>& in, const UnownedStringSlice& separator, StringBuilder& out);
diff --git a/source/core/slang-type-text-util.cpp b/source/core/slang-type-text-util.cpp
index 172426bba..6743800b3 100644
--- a/source/core/slang-type-text-util.cpp
+++ b/source/core/slang-type-text-util.cpp
@@ -7,9 +7,6 @@
namespace Slang
{
-
-
-
namespace { // anonymous
#define SLANG_SCALAR_TYPES(x) \
@@ -66,17 +63,16 @@ static const TypeTextUtil::CompileTargetInfo s_compileTargetInfos[] =
{ SLANG_HOST_HOST_CALLABLE, "", "host-host-callable", "Host callable for host execution" },
};
-static const NamesValue s_languageInfos[] =
+static const NamesDescriptionValue s_languageInfos[] =
{
- { SLANG_SOURCE_LANGUAGE_C, "c,C" },
- { SLANG_SOURCE_LANGUAGE_CPP, "cpp,c++,C++,cxx" },
- { SLANG_SOURCE_LANGUAGE_SLANG, "slang" },
- { SLANG_SOURCE_LANGUAGE_GLSL, "glsl" },
- { SLANG_SOURCE_LANGUAGE_HLSL, "hlsl" },
- { SLANG_SOURCE_LANGUAGE_CUDA, "cu,cuda" },
+ { SLANG_SOURCE_LANGUAGE_C, "c,C", "C language" },
+ { SLANG_SOURCE_LANGUAGE_CPP, "cpp,c++,C++,cxx", "C++ language" },
+ { SLANG_SOURCE_LANGUAGE_SLANG, "slang", "Slang language" },
+ { SLANG_SOURCE_LANGUAGE_GLSL, "glsl", "GLSL language" },
+ { SLANG_SOURCE_LANGUAGE_HLSL, "hlsl", "HLSL language" },
+ { SLANG_SOURCE_LANGUAGE_CUDA, "cu,cuda", "CUDA" },
};
-
static const NamesDescriptionValue s_compilerInfos[] =
{
{ SLANG_PASS_THROUGH_NONE, "none", "Unknown" },
@@ -91,15 +87,14 @@ static const NamesDescriptionValue s_compilerInfos[] =
{ SLANG_PASS_THROUGH_LLVM, "llvm", "LLVM/Clang `slang-llvm`" },
};
-static const NameValue s_archiveTypeInfos[] =
+static const NamesDescriptionValue s_archiveTypeInfos[] =
{
- { SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, "riff-deflate"},
- { SLANG_ARCHIVE_TYPE_RIFF_LZ4, "riff-lz4"},
- { SLANG_ARCHIVE_TYPE_ZIP, "zip"},
- { SLANG_ARCHIVE_TYPE_RIFF, "riff"},
+ { SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, "riff-deflate", "Slang RIFF using deflate compression" },
+ { SLANG_ARCHIVE_TYPE_RIFF_LZ4, "riff-lz4", "Slang RIFF using LZ4 compression" },
+ { SLANG_ARCHIVE_TYPE_ZIP, "zip", "Zip file" },
+ { SLANG_ARCHIVE_TYPE_RIFF, "riff", "Slang RIFF without compression" },
};
-
static const NamesDescriptionValue s_debugInfoFormatInfos[] =
{
{ SLANG_DEBUG_INFO_FORMAT_DEFAULT, "default-format", "Use the default debugging format for the target" },
@@ -134,14 +129,42 @@ static const NamesDescriptionValue s_floatingPointModes[] =
"Default floating point mode" }
};
+static const NamesDescriptionValue s_optimizationLevels[] =
+{
+ { SLANG_OPTIMIZATION_LEVEL_NONE, "0,none", "Disable all optimizations" },
+ { SLANG_OPTIMIZATION_LEVEL_DEFAULT, "1,default", "Enable a default level of optimization.This is the default if no -o options are used." },
+ { SLANG_OPTIMIZATION_LEVEL_HIGH, "2,high", "Enable aggressive optimizations for speed." },
+ { SLANG_OPTIMIZATION_LEVEL_MAXIMAL, "3,maximal", "Enable further optimizations, which might have a significant impact on compile time, or involve unwanted tradeoffs in terms of code size." },
+};
+
+static const NamesDescriptionValue s_debugLevels[] =
+{
+ { SLANG_DEBUG_INFO_LEVEL_NONE, "0,none", "Don't emit debug information at all." },
+ { SLANG_DEBUG_INFO_LEVEL_MINIMAL, "1,minimal", "Emit as little debug information as possible, while still supporting stack traces." },
+ { SLANG_DEBUG_INFO_LEVEL_STANDARD, "2,standard", "Emit whatever is the standard level of debug information for each target." },
+ { SLANG_DEBUG_INFO_LEVEL_MAXIMAL, "3,maximal", "Emit as much debug information as possible for each target." },
+};
+
+static const NamesDescriptionValue s_fileSystemTypes[] =
+{
+ { ValueInt(TypeTextUtil::FileSystemType::Default), "default", "Default fike system." },
+ { ValueInt(TypeTextUtil::FileSystemType::LoadFile), "load-file", "Just implements loadFile interface, so will be wrapped with CacheFileSystem internally." },
+ { ValueInt(TypeTextUtil::FileSystemType::Os), "os", "Use the OS based file system directly (without file system caching)" },
+};
+
} // anonymous
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getFileSystemTypeInfos()
+{
+ return makeConstArrayView(s_fileSystemTypes);
+}
+
/* static */ConstArrayView<TypeTextUtil::CompileTargetInfo> TypeTextUtil::getCompileTargetInfos()
{
return makeConstArrayView(s_compileTargetInfos);
}
-/* static */ConstArrayView<NamesValue> TypeTextUtil::getLanguageInfos()
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getLanguageInfos()
{
return makeConstArrayView(s_languageInfos);
}
@@ -151,7 +174,7 @@ static const NamesDescriptionValue s_floatingPointModes[] =
return makeConstArrayView(s_compilerInfos);
}
-/* static */ConstArrayView<NameValue> TypeTextUtil::getArchiveTypeInfos()
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getArchiveTypeInfos()
{
return makeConstArrayView(s_archiveTypeInfos);
}
@@ -171,6 +194,16 @@ static const NamesDescriptionValue s_floatingPointModes[] =
return makeConstArrayView(s_floatingPointModes);
}
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getOptimizationLevelInfos()
+{
+ return makeConstArrayView(s_optimizationLevels);
+}
+
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getDebugLevelInfos()
+{
+ return makeConstArrayView(s_debugLevels);
+}
+
/* static */SlangArchiveType TypeTextUtil::findArchiveType(const UnownedStringSlice& slice)
{
return NameValueUtil::findValue(getArchiveTypeInfos(), slice, SLANG_ARCHIVE_TYPE_UNDEFINED);
diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h
index 2249b1215..cf146fb46 100644
--- a/source/core/slang-type-text-util.h
+++ b/source/core/slang-type-text-util.h
@@ -13,6 +13,13 @@ namespace Slang
/// Utility class to allow conversion of types (such as enums) to and from text types
struct TypeTextUtil
{
+ enum class FileSystemType
+ {
+ Default,
+ LoadFile,
+ Os,
+ };
+
struct CompileTargetInfo
{
SlangCompileTarget target; ///< The target
@@ -25,17 +32,23 @@ struct TypeTextUtil
static ConstArrayView<CompileTargetInfo> getCompileTargetInfos();
/// Get the language infos
- static ConstArrayView<NamesValue> getLanguageInfos();
+ static ConstArrayView<NamesDescriptionValue> getLanguageInfos();
/// Get the compiler infos
static ConstArrayView<NamesDescriptionValue> getCompilerInfos();
/// Get the archive type infos
- static ConstArrayView<NameValue> getArchiveTypeInfos();
+ static ConstArrayView<NamesDescriptionValue> getArchiveTypeInfos();
/// Get the debug format types
static ConstArrayView<NamesDescriptionValue> getDebugInfoFormatInfos();
-
+ /// Get the debug levels
+ static ConstArrayView<NamesDescriptionValue> getDebugLevelInfos();
+ /// Get the floating point modes
static ConstArrayView<NamesDescriptionValue> getFloatingPointModeInfos();
-
+ // Get the line directive infos
static ConstArrayView<NamesDescriptionValue> getLineDirectiveInfos();
+ /// Get the optimization level info
+ static ConstArrayView<NamesDescriptionValue> getOptimizationLevelInfos();
+ /// Get the file system type infos
+ static ConstArrayView<NamesDescriptionValue> getFileSystemTypeInfos();
/// Get the scalar type as text.
static Slang::UnownedStringSlice getScalarTypeName(slang::TypeReflection::ScalarType scalarType);
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 8738c71cc..0b6494ad5 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -70,14 +70,12 @@ DIAGNOSTIC( 14, Error, unknownProfile, "unknown profile '$0'")
DIAGNOSTIC( 15, Error, unknownStage, "unknown stage '$0'")
DIAGNOSTIC( 16, Error, unknownPassThroughTarget, "unknown pass-through target '$0'")
DIAGNOSTIC( 17, Error, unknownCommandLineOption, "unknown command-line option '$0'")
-DIAGNOSTIC( 18, Error, unknownFileSystemOption, "unknown file-system option '$0'")
DIAGNOSTIC( 19, Error, unknownSourceLanguage, "unknown source language '$0'")
DIAGNOSTIC( 20, Error, entryPointsNeedToBeAssociatedWithTranslationUnits, "when using multiple source files, entry points must be specified after their corresponding source file(s)")
DIAGNOSTIC( 22, Error, unknownDownstreamCompiler, "unknown downstream compiler '$0'")
DIAGNOSTIC( 26, Error, unknownOptimiziationLevel, "unknown optimization level '$0'")
-DIAGNOSTIC( 27, Error, unknownDebugInfoLevel, "unknown debug info level '$0'")
DIAGNOSTIC( 28, Error, unableToGenerateCodeForTarget, "unable to generate code for target '$0'")
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 6cb53496f..d6486ba18 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -32,7 +32,9 @@
#include "../core/slang-string-slice-pool.h"
#include "../core/slang-char-util.h"
-#include "../core/slang-command-options.h"
+#include "../core/slang-name-value.h"
+
+#include "../core/slang-command-options-writer.h"
#include <assert.h>
@@ -49,6 +51,7 @@ enum class OptionKind
DepFile,
EntryPointName,
Help,
+ HelpStyle,
Include,
Language,
MatrixLayoutColumn,
@@ -151,6 +154,10 @@ enum class ValueCategory
Stage,
LineDirectiveMode,
DebugInfoFormat,
+ HelpStyle,
+ OptimizationLevel,
+ DebugLevel,
+ FileSystemType,
CountOf,
};
@@ -200,6 +207,18 @@ void initCommandOptions(CommandOptions& options)
options.addCategory(CategoryKind::Value, "fp-mode", "Floating Point Mode", UserValue(ValueCategory::FloatingPointMode));
options.addValues(TypeTextUtil::getFloatingPointModeInfos());
+
+ options.addCategory(CategoryKind::Value, "help-style", "Help Style", UserValue(ValueCategory::HelpStyle));
+ options.addValues(CommandOptionsWriter::getStyleInfos());
+
+ options.addCategory(CategoryKind::Value, "optimization-level", "Optimization Level", UserValue(ValueCategory::OptimizationLevel));
+ options.addValues(TypeTextUtil::getOptimizationLevelInfos());
+
+ options.addCategory(CategoryKind::Value, "debug-level", "Debug Level", UserValue(ValueCategory::DebugLevel));
+ options.addValues(TypeTextUtil::getDebugLevelInfos());
+
+ options.addCategory(CategoryKind::Value, "file-system-type", "File System Type", UserValue(ValueCategory::FileSystemType));
+ options.addValues(TypeTextUtil::getFileSystemTypeInfos());
}
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! target !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
@@ -307,15 +326,20 @@ void initCommandOptions(CommandOptions& options)
const Option generalOpts[] =
{
- { OptionKind::MacroDefine, "-D?...", "-D<name>[=<value>], -D <name>[=<value>]", "Insert a preprocessor macro." },
+ { OptionKind::MacroDefine, "-D?...", "-D<name>[=<value>], -D <name>[=<value>]",
+ "Insert a preprocessor macro.\n"
+ "The space between - D and <name> is optional. If no <value> is specified, Slang will define the macro with an empty value." },
{ OptionKind::DepFile, "-depfile", "-depfile <path>", "Save the source file dependency list in a file." },
{ OptionKind::EntryPointName, "-entry", "-entry <name>",
"Specify the name of an entry-point function.\n"
+ "When compiling from a single file, this defaults to main if you specify a stage using -stage.\n"
"Multiple -entry options may be used in a single invocation. "
+ "When they do, the file associated with the entry point will be the first one found when searching to the left in the command line.\n"
"If no -entry options are given, compiler will use [shader(...)] "
"attributes to detect entry points."},
{ OptionKind::EmitIr, "-emit-ir", nullptr, "Emit IR typically as a '.slang-module' when outputting to a container." },
{ OptionKind::Help, "-h,-help,--help", "-h or -h <help-category>", "Print this message, or help in specified category." },
+ { OptionKind::HelpStyle, "-help-style", "-help-style <help-style>", "Help formatting style" },
{ OptionKind::Include, "-I?...", "-I<path>, -I <path>",
"Add a path to be used in resolving '#include' "
"and 'import' operations."},
@@ -336,10 +360,10 @@ void initCommandOptions(CommandOptions& options)
{ OptionKind::Profile, "-profile", "-profile <profile>[+<capability>...]",
"Specify the shader profile for code generation.\n"
"Accepted profiles are:\n"
- " sm_{4_0,4_1,5_0,5_1,6_0,6_1,6_2,6_3,6_4,6_5,6_6}\n"
- " glsl_{110,120,130,140,150,330,400,410,420,430,440,450,460}\n"
+ "* sm_{4_0,4_1,5_0,5_1,6_0,6_1,6_2,6_3,6_4,6_5,6_6}\n"
+ "* glsl_{110,120,130,140,150,330,400,410,420,430,440,450,460}\n"
"Additional profiles that include -stage information:\n"
- " {vs,hs,ds,gs,ps}_<version>\n"
+ "* {vs,hs,ds,gs,ps}_<version>\n"
"See -capability for information on <capability>\n"
"When multiple -target options are present, each -profile associates "
"with the first -target to its left."},
@@ -350,7 +374,9 @@ void initCommandOptions(CommandOptions& options)
"May be omitted if entry-point function has a [shader(...)] attribute; "
"otherwise required for each -entry option."},
{ OptionKind::Target, "-target", "-target <target>", "Specifies the format in which code should be generated."},
- { OptionKind::Version, "-v,-version", nullptr, "Display the build version."},
+ { OptionKind::Version, "-v,-version", nullptr,
+ "Display the build version. This is the contents of git describe --tags.\n"
+ "It is typically only set from automated builds(such as distros available on github).A user build will by default be 'unknown'."},
{ OptionKind::WarningsAsErrors, "-warnings-as-errors", "-warnings-as-errors all or -warnings-as-errors <id>[,<id>...]",
"all - Treat all warnings as errors.\n"
"<id>[,<id>...]: Treat specific warning ids as errors.\n"},
@@ -377,19 +403,17 @@ void initCommandOptions(CommandOptions& options)
{ OptionKind::DisableSpecialization, "-disable-specialization", nullptr, "Disables generics and specialization pass." },
{ OptionKind::FloatingPointMode, "-fp-mode,-floating-point-mode", "-fp-mode <fp-mode>, -floating-point-mode <fp-mode>",
"Control floating point optimizations"},
- { OptionKind::DebugInformation, "-g...", "-g, -g<N>, -g<debug-info-format>",
+ { OptionKind::DebugInformation, "-g...", "-g, -g<debug-info-format>, -g<debug-level>",
"Include debug information in the generated code, where possible.\n"
- "N is the amount of information, 0..3, unspecified means 2\n"
+ "<debug-level> is the amount of information, 0..3, unspecified means 2\n"
"<debug-info-format> specifies a debugging info format\n"
- "It is valid to have multiple -g options, such as a level and a <debug-info-format>" },
+ "It is valid to have multiple -g options, such as a <debug-level> and a <debug-info-format>" },
{ OptionKind::LineDirectiveMode, "-line-directive-mode", "-line-directive-mode <line-directive-mode>",
"Sets how the `#line` directives should be produced. Available options are:\n"
"If not specified, default behavior is to use C-style `#line` directives "
"for HLSL and C/C++ output, and traditional GLSL-style `#line` directives "
"for GLSL output." },
- { OptionKind::Optimization, "-O...", "-O<N>",
- "Set the optimization level.\n"
- "N is the amount of optimization, 0..3, default is 1" },
+ { OptionKind::Optimization, "-O...", "-O<optimization-level>", "Set the optimization level."},
{ OptionKind::Obfuscate, "-obfuscate", nullptr, "Remove all source file information from outputs." },
};
@@ -424,8 +448,9 @@ void initCommandOptions(CommandOptions& options)
"to the downstream compiler. -X<compiler>... options -X. will pass *all* of the options "
"inbetween the opening -X and -X. to the downstream compiler."},
{ OptionKind::PassThrough, "-pass-through", "-pass-through <compiler>",
- "Pass the input through mostly unmodified to the \n"
- "existing compiler <compiler>." },
+ "Pass the input through mostly unmodified to the "
+ "existing compiler <compiler>.\n"
+ "These are intended for debugging/testing purposes, when you want to be able to see what these existing compilers do with the \"same\" input and options"},
};
_addOptions(makeConstArrayView(downstreamOpts), options);
@@ -470,9 +495,8 @@ void initCommandOptions(CommandOptions& options)
{ OptionKind::EmitSpirvDirectly, "-emit-spirv-directly", nullptr,
"Generate SPIR-V output directly (otherwise through "
"GLSL and using the glslang compiler)"},
- { OptionKind::FileSystem, "-file-system", "-file-system <fs>",
- "Set the filesystem hook to use for a compile request.\n"
- "Accepted file systems: default, load-file, os" },
+ { OptionKind::FileSystem, "-file-system", "-file-system <file-system-type>",
+ "Set the filesystem hook to use for a compile request."},
{ OptionKind::Heterogeneous, "-heterogeneous", nullptr, "Output heterogeneity-related code." },
{ OptionKind::NoMangle, "-no-mangle", nullptr, "Do as little mangling of names as possible." }
};
@@ -1087,11 +1111,11 @@ struct OptionsParser
return SLANG_OK;
}
- SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
+ SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, const UnownedStringSlice& name, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
{
auto& cmdOptions = asInternal(session)->m_commandOptions;
- const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), arg.value.getUnownedSlice());
+ const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), name);
if (optionIndex < 0)
{
const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory));
@@ -1114,6 +1138,46 @@ struct OptionsParser
outValue = cmdOptions.getOptionAt(optionIndex).userValue;
return SLANG_OK;
}
+
+ SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
+ {
+ return _getValue(valueCategory, arg, arg.value.getUnownedSlice(), sink, outValue);
+ }
+
+ SlangResult _getValue(const ConstArrayView<ValueCategory>& valueCategories, const CommandLineArg& arg, const UnownedStringSlice& name, DiagnosticSink* sink, ValueCategory& outCat, CommandOptions::UserValue& outValue)
+ {
+ auto& cmdOptions = asInternal(session)->m_commandOptions;
+
+ for (auto valueCategory : valueCategories)
+ {
+ const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), name);
+ if (optionIndex >= 0)
+ {
+ outCat = valueCategory;
+ outValue = cmdOptions.getOptionAt(optionIndex).userValue;
+ return SLANG_OK;
+ }
+ }
+
+ List<UnownedStringSlice> names;
+ for (auto valueCategory : valueCategories)
+ {
+ const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory));
+ SLANG_ASSERT(categoryIndex >= 0);
+ if (categoryIndex < 0)
+ {
+ return SLANG_FAIL;
+ }
+ cmdOptions.appendCategoryOptionNames(categoryIndex, names);
+ }
+
+ StringBuilder buf;
+ StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), buf);
+
+ sink->diagnose(arg.loc, Diagnostics::unknownCommandLineValue, buf);
+ return SLANG_FAIL;
+ }
+
SlangResult _expectValue(ValueCategory valueCategory, CommandLineReader& reader, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
{
CommandLineArg arg;
@@ -1208,7 +1272,8 @@ struct OptionsParser
// Get the options on the session
CommandOptions& options = asInternal(session)->m_commandOptions;
-
+ CommandOptionsWriter::Style helpStyle = CommandOptionsWriter::Style::Text;
+
auto frontEndReq = requestImpl->getFrontEndReq();
while (reader.hasArg())
@@ -1771,86 +1836,45 @@ struct OptionsParser
UnownedStringSlice levelSlice = argValue.getUnownedSlice().tail(2);
SlangOptimizationLevel level = SLANG_OPTIMIZATION_LEVEL_DEFAULT;
- const char c = levelSlice.getLength() == 1 ? levelSlice[0] : 0;
-
- switch (c)
+ if (levelSlice.getLength())
{
- case '0': level = SLANG_OPTIMIZATION_LEVEL_NONE; break;
- case '1': level = SLANG_OPTIMIZATION_LEVEL_DEFAULT; break;
- case '2': level = SLANG_OPTIMIZATION_LEVEL_HIGH; break;
- case '3': level = SLANG_OPTIMIZATION_LEVEL_MAXIMAL; break;
- default:
- {
- sink->diagnose(arg.loc, Diagnostics::unknownOptimiziationLevel, arg.value);
- return SLANG_FAIL;
- }
+ CommandOptions::UserValue value;
+ SLANG_RETURN_ON_FAIL(_getValue(ValueCategory::OptimizationLevel, arg, levelSlice, sink, value));
+ level = SlangOptimizationLevel(value);
}
-
+
compileRequest->setOptimizationLevel(level);
break;
}
case OptionKind::DebugInformation:
{
+ auto name = argValue.getUnownedSlice().tail(2);
+
// Note: unlike with `-O` above, we have to consider that other
// options might have names that start with `-g` and so cannot
// just detect it as a prefix.
- if (argValue == toSlice("-g"))
+ if (name.getLength() == 0)
{
// The default is standard
compileRequest->setDebugInfoLevel(SLANG_DEBUG_INFO_LEVEL_STANDARD);
}
- else if (argValue.getLength() == 3 && argValue[2] >= '0' && argValue[2] <= '3')
+ else
{
- // Extract the digit into an index
- const Index levelIndex = argValue[2] - '0';
- SLANG_ASSERT(levelIndex >= 0 && levelIndex <= 3);
+ CommandOptions::UserValue value;
+ ValueCategory valueCat;
+ ValueCategory valueCats[] = { ValueCategory::DebugLevel, ValueCategory::DebugInfoFormat };
+ SLANG_RETURN_ON_FAIL(_getValue(makeConstArrayView(valueCats), arg, name, sink, valueCat, value));
- // Map indices to enum values
- const SlangDebugInfoLevel levels[] =
+ if (valueCat == ValueCategory::DebugLevel)
{
- SLANG_DEBUG_INFO_LEVEL_NONE,
- SLANG_DEBUG_INFO_LEVEL_MINIMAL,
- SLANG_DEBUG_INFO_LEVEL_STANDARD,
- SLANG_DEBUG_INFO_LEVEL_MAXIMAL
- };
-
- const auto level = levels[levelIndex];
- compileRequest->setDebugInfoLevel(level);
- }
- else
- {
- // Perhaps it's trying to specify a format
- auto formatName = argValue.getUnownedSlice().tail(2);
-
- SlangDebugInfoFormat format;
- if (SLANG_FAILED(TypeTextUtil::findDebugInfoFormat(formatName, format)))
+ const auto level = (SlangDebugInfoLevel)value;
+ compileRequest->setDebugInfoLevel(level);
+ }
+ else
{
- List<String> debugOptions;
-
- debugOptions.add(toSlice("-g"));
-
- for (Int i = 0; i <= 3; ++i)
- {
- StringBuilder buf;
- buf << toSlice("-g") << i;
- debugOptions.add(buf);
- }
-
- for (Index i = 0; i < SLANG_DEBUG_INFO_FORMAT_COUNT_OF; ++i)
- {
- StringBuilder buf;
- buf << toSlice("-g") << TypeTextUtil::getDebugInfoFormatName(SlangDebugInfoFormat(i));
- debugOptions.add(buf);
- }
-
- StringBuilder buf;
- StringUtil::join(debugOptions, toSlice(", "), buf);
-
- sink->diagnose(arg.loc, Diagnostics::unknownDebugOption, buf);
- return SLANG_FAIL;
+ const auto debugFormat = (SlangDebugInfoFormat)value;
+ compileRequest->setDebugInfoFormat(debugFormat);
}
-
- compileRequest->setDebugInfoFormat(format);
}
break;
}
@@ -1858,27 +1882,15 @@ struct OptionsParser
case OptionKind::Obfuscate: requestImpl->getLinkage()->m_obfuscateCode = true; break;
case OptionKind::FileSystem:
{
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
+ CommandOptions::UserValue value;
+ SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::FileSystemType, reader, sink, value));
+ typedef TypeTextUtil::FileSystemType FileSystemType;
- if (name.value == "default")
- {
- compileRequest->setFileSystem(nullptr);
- }
- else if (name.value == "load-file")
+ switch (FileSystemType(value))
{
- // 'Simple' just implements loadFile interface, so will be wrapped with CacheFileSystem internally
- compileRequest->setFileSystem(OSFileSystem::getLoadSingleton());
- }
- else if (name.value == "os")
- {
- // 'Immutable' implements the ISlangFileSystemExt interface - and will be used directly
- compileRequest->setFileSystem(OSFileSystem::getExtSingleton());
- }
- else
- {
- sink->diagnose(name.loc, Diagnostics::unknownFileSystemOption, name.value);
- return SLANG_FAIL;
+ case FileSystemType::Default: compileRequest->setFileSystem(nullptr); break;
+ case FileSystemType::LoadFile: compileRequest->setFileSystem(OSFileSystem::getLoadSingleton()); break;
+ case FileSystemType::Os: compileRequest->setFileSystem(OSFileSystem::getExtSingleton()); break;
}
break;
}
@@ -1950,9 +1962,15 @@ struct OptionsParser
sink->diagnoseRaw(Severity::Note, session->getBuildTagString());
break;
}
+ case OptionKind::HelpStyle:
+ {
+ CommandOptions::UserValue value;
+ SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::HelpStyle, reader, sink, value));
+ helpStyle = CommandOptionsWriter::Style(value);
+ break;
+ }
case OptionKind::Help:
{
-
Index categoryIndex = -1;
if (reader.hasArg())
@@ -1960,24 +1978,48 @@ struct OptionsParser
auto catArg = reader.getArgAndAdvance();
categoryIndex = options.findCategoryByCaseInsensitiveName(catArg.value.getUnownedSlice());
- if (categoryIndex)
+ if (categoryIndex < 0)
{
sink->diagnose(catArg.loc, Diagnostics::unknownHelpCategory);
return SLANG_FAIL;
}
}
- CommandOptionsWriter writer;
- auto& buf = writer.getBuilder();
+ CommandOptionsWriter::Options writerOptions;
+ writerOptions.style = helpStyle;
+
+ auto writer = CommandOptionsWriter::create(writerOptions);
+
+ auto& buf = writer->getBuilder();
if (categoryIndex < 0)
{
- _appendUsageTitle(buf);
- writer.appendDescription(options);
+ // If it's the text style we can inject usage at the top
+ if (helpStyle == CommandOptionsWriter::Style::Text)
+ {
+ _appendUsageTitle(buf);
+ }
+ else
+ {
+ // NOTE! We need this preamble because if we have links,
+ // we have to make sure the first thing in markdown *isn't* <>
+
+ buf << "# Slang Command Line Options\n\n";
+ buf << "*Usage:*\n";
+ buf << "```\n";
+ buf << "slangc [options...] [--] <input files>\n\n";
+ buf << "# For help\n";
+ buf << "slangc -h\n\n";
+ buf << "# To generate this file\n";
+ buf << "slangc -help-style markdown -h\n";
+ buf << "```\n";
+ }
+
+ writer->appendDescription(&options);
}
else
{
- writer.appendDescriptionForCategory(options, categoryIndex);
+ writer->appendDescriptionForCategory(&options, categoryIndex);
}
sink->diagnoseRaw(Severity::Note, buf.getBuffer());