summaryrefslogtreecommitdiff
path: root/source/slang/slang-doc-extractor.h
blob: 7a33b390af7ca147d1adc3aad3e5e1158db97b25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// slang-doc.h
#ifndef SLANG_DOC_EXTRACTOR_H
#define SLANG_DOC_EXTRACTOR_H

#include "../core/slang-basic.h"
#include "slang-ast-all.h"

namespace Slang {

enum class MarkupVisibility : uint8_t
{
    Public,                 ///< Always available
    Internal,               ///< Can be available in more verbose 'internal' documentation
    Hidden,                 ///< Not generally available
};

/* Holds the documentation markup that is associated with each node (typically a decl) from a module */
class DocMarkup : public RefObject
{
public:
    struct Entry
    {
        NodeBase* m_node;                                           ///< The node this documentation is associated with
        String m_markup;                                            ///< The raw contents of of markup associated with the decoration
        MarkupVisibility m_visibility = MarkupVisibility::Public;   ///< How visible this decl is
    };

        /// Adds an entry, returns the reference to pre-existing node if there is one
    Entry& addEntry(NodeBase* base);
        /// Gets an entry for a node. Returns nullptr if there is no markup.
    Entry* getEntry(NodeBase* base);

        /// Get list of all of the entries in source order
    const List<Entry>& getEntries() const { return m_entries; }

protected:

        /// Map from AST nodes to documentation entries
    Dictionary<NodeBase*, Index> m_entryMap;
        /// All of the documentation entries in source order
    List<Entry> m_entries;
};

// ---------------------------------------------------------------------------
SLANG_INLINE DocMarkup::Entry& DocMarkup::addEntry(NodeBase* base)
{
    const Index count = m_entries.getCount();
    const Index index = m_entryMap.GetOrAddValue(base, count);

    if (index == count)
    {
        Entry entry;
        entry.m_node = base;
        m_entries.add(entry);
    }
    return m_entries[index];
}

// ---------------------------------------------------------------------------
SLANG_INLINE DocMarkup::Entry* DocMarkup::getEntry(NodeBase* base)
{
    Index* indexPtr = m_entryMap.TryGetValue(base);
    return (indexPtr) ? &m_entries[*indexPtr] : nullptr;
}

/* Extracts 'markup' from comments in Slang source core. The comments are extracted and associated in declarations. The association
is held in DocMarkup type. The comment style follows the doxygen style */
class DocMarkupExtractor
{
public:

    typedef uint32_t MarkupFlags;
    struct MarkupFlag
    {
        enum Enum : MarkupFlags
        {
            Before = 0x1,
            After = 0x2,
            IsMultiToken = 0x4,          ///< Can use more than one token
            IsBlock = 0x8,          ///< 
        };
    };

    // NOTE! Don't change order without fixing isBefore and isAfter
    enum class MarkupType
    {
        None,

        BlockBefore,        /// /**  */ or /*!  */.
        LineBangBefore,      /// //! Can be multiple lines
        LineSlashBefore,     /// /// Can be multiple lines

        BlockAfter,         /// /*!< */ or /**< */
        LineBangAfter,       /// //!< Can be multiple lines
        LineSlashAfter,      /// ///< Can be multiple lines
    };

    static bool isBefore(MarkupType type) { return Index(type) >= Index(MarkupType::BlockBefore) && Index(type) <= Index(MarkupType::LineSlashBefore); }
    static bool isAfter(MarkupType type) { return Index(type) >= Index(MarkupType::BlockAfter); }

    struct IndexRange
    {
        SLANG_FORCE_INLINE Index getCount() const { return end - start; }

        Index start;
        Index end;
    };

    enum class Location
    {
        None,                           ///< No defined location
        Before,
        AfterParam,                     ///< Can have trailing , or )
        AfterSemicolon,                 ///< Can have a trailing ;
        AfterEnumCase,                  ///< Can have a , or before }
        AfterGenericParam,              ///< Can have trailing , or > 
    };

    static bool isAfter(Location location) { return Index(location) >= Index(Location::AfterParam); }
    static bool isBefore(Location location) { return location == Location::Before; }

    struct FoundMarkup
    {
        void reset()
        {
            location = Location::None;
            type = MarkupType::None;
            range = IndexRange{ 0, 0 };
        }

        Location location = Location::None;
        MarkupType type = MarkupType::None;
        IndexRange range;
    };

    enum SearchStyle
    {
        None,               ///< Cannot be searched for
        EnumCase,           ///< An enum case
        Param,              ///< A parameter in a function/method
        Variable,           ///< A variable-like declaration
        Before,             ///< Only allows before
        Function,           ///< Function/method
        GenericParam,       ///< Generic parameter
    };

        /// An input search item
    struct SearchItemInput
    {
        SourceLoc sourceLoc;
        SearchStyle searchStyle;            ///< The search style when looking for an item
    };

        /// The items will be in source order
    struct SearchItemOutput
    {
        Index viewIndex;                    ///< Index into the array of views on the output
        Index inputIndex;                   ///< The index to this item in the input
        String text;                        ///< The found text
        MarkupVisibility visibilty;         ///< Visibility of the item
    };

    struct FindInfo
    {
        SourceView* sourceView;         ///< The source view the tokens were generated from
        TokenList* tokenList;           ///< The token list
        Index tokenIndex;               ///< The token index location (where searches start from)
        Index lineIndex;                ///< The line number for the decl
    };

        /// Extracts documentation from the nodes held in the module using the source manager. Found documentation is placed
        /// in outMarkup
    static SlangResult extract(ModuleDecl* moduleDecl, SourceManager* sourceManager, DiagnosticSink* sink, DocMarkup* outMarkup);

        /// Extracts 'markup' doc information for the specified input items
        /// The output is placed in out - with the items now in the source order *not* the order of the input items
        /// The inputIndex on the output holds the input item index
        /// The outViews holds the views specified in viewIndex in the output, which may be useful for determining where the documentation was placed in source
    SlangResult extract(const SearchItemInput* inputItems, Index inputCount, SourceManager* sourceManager, DiagnosticSink* sink, List<SourceView*>& outViews, List<SearchItemOutput>& out);

        /// Given a module finds all the decls, and places in outDecls
    static void findDecls(ModuleDecl* moduleDecl, List<Decl*>& outDecls);

        /// Given a decl determines the search style that is appropriate. Returns None if can't determine a suitable style
    static SearchStyle getSearchStyle(Decl* decl);
    
    static MarkupFlags getFlags(MarkupType type);
    static MarkupType findMarkupType(const Token& tok);
    static UnownedStringSlice removeStart(MarkupType type, const UnownedStringSlice& comment);

protected:
    /// returns SLANG_E_NOT_FOUND if not found, SLANG_OK on success else an error
    SlangResult _findMarkup(const FindInfo& info, Location location, FoundMarkup& out);

    /// Locations are processed in order, and the first successful used. If found in another location will issue a warning.
    /// returns SLANG_E_NOT_FOUND if not found, SLANG_OK on success else an error
    SlangResult _findFirstMarkup(const FindInfo& info, const Location* locs, Index locCount, FoundMarkup& out, Index& outIndex);

    SlangResult _findMarkup(const FindInfo& info, const Location* locs, Index locCount, FoundMarkup& out);

    /// Given the decl, the token stream, and the decls tokenIndex, try to find some associated markup
    SlangResult _findMarkup(const FindInfo& info, SearchStyle searchStyle, FoundMarkup& out);

    /// Given a found markup location extracts the contents of the tokens into out
    SlangResult _extractMarkup(const FindInfo& info, const FoundMarkup& foundMarkup, StringBuilder& out);

    /// Given a location, try to find the first token index that could potentially be markup
    /// Will return -1 if not found
    Index _findStartIndex(const FindInfo& info, Location location);

    /// True if the tok is 'on' lineIndex. Interpretation of 'on' depends on the markup type.
    static bool _isTokenOnLineIndex(SourceView* sourceView, MarkupType type, const Token& tok, Index lineIndex);

    DiagnosticSink* m_sink;
};

} // namespace Slang

#endif