Add auto-completion prompts for filter function parameters

This commit is contained in:
baldurk
2021-07-01 15:15:03 +01:00
parent 3bc221d625
commit cb7fdc9b94
4 changed files with 173 additions and 16 deletions
+28 -2
View File
@@ -228,6 +228,26 @@ DOCUMENT(R"(The event browser window.
:return: An empty string if the parse succeeded, otherwise any error messages to be displayed to
the user, such as syntax or other errors.
:rtype: str
.. function:: AutoCompleteCallback(context, filter, params)
Not a member function - the signature for any ``AutoCompleteCallback`` callbacks.
Called when autocompletion is triggered inside a filter. The params passed are any previous
text inside the filter's parameter list up to where the cursor is. The callback should return a
list of identifiers used for auto-completion.
The list does not have to be pre-filtered for matches to the :paramref:`params`, that is provided
to allow different autocompletion at different stages (e.g. if there are no parameters, you can
autocomplete a property, if a property is already present you can autocomplete valid values for
it)
:param CaptureContext context: The current capture context.
:param str filter: The name of the filter function.
:param str params: The previous parameter text to the filter function.
:return: A list of strings giving identifiers to autocomplete, or an empty list of there are no
such identifiers to prompt.
:rtype: List[str]
)");
struct IEventBrowser
{
@@ -237,6 +257,9 @@ struct IEventBrowser
typedef std::function<rdcstr(ICaptureContext *, const rdcstr &, const rdcstr &)> FilterParseCallback;
typedef std::function<rdcarray<rdcstr>(ICaptureContext *, const rdcstr &, const rdcstr &)>
AutoCompleteCallback;
DOCUMENT(R"(Retrieves the PySide2 QWidget for this :class:`EventBrowser` if PySide2 is available, or otherwise
returns a unique opaque pointer that can be passed back to any RenderDoc functions expecting a
QWidget.
@@ -289,12 +312,15 @@ expression.
:param EventFilterCallback filter: The callback to call for each candidate event to perform
filtering.
:param FilterParseCallback parser: The callback to call when the parsing the parameters and checking
for any errors.
for any errors. This can be ``None`` if no pre-parsing is required.
:param AutoCompleteCallback completer: The callback to call when trying to provide autocomplete
suggestions. This can be ``None`` if no completion is desired/applicable.
:return: Whether or not the registration was successful.
:rtype: bool
)");
virtual bool RegisterEventFilterFunction(const rdcstr &name, EventFilterCallback filter,
FilterParseCallback parser) = 0;
FilterParseCallback parser,
AutoCompleteCallback completer) = 0;
DOCUMENT(R"(Unregisters an event browser filter function that was previously registered.
+11 -3
View File
@@ -232,7 +232,6 @@ void RDTextEdit::keyPressEvent(QKeyEvent *e)
{
QTextEdit::keyPressEvent(e);
}
emit(keyPress(e));
// stop completing if the character just entered is not a word-compatible character
if(completionInProgress() && e->text().length() > 0)
@@ -240,11 +239,20 @@ void RDTextEdit::keyPressEvent(QKeyEvent *e)
QChar c = e->text()[0];
if(c.isPrint() && !e->text()[0].isLetterOrNumber() && !m_WordCharacters.contains(e->text()[0]))
{
m_Completer->popup()->hide();
emit(completionEnd());
if(c.isSpace() && m_Completer->completionPrefix().trimmed() == QString())
{
// don't do anything if we have no prefix so far and the user enters whitespace
}
else
{
m_Completer->popup()->hide();
emit(completionEnd());
}
}
}
emit(keyPress(e));
// update the completion if it's in progress, or we have our shortcut (and there's no selected
// text)
if((completionShortcut && !textCursor().hasSelection()) || completionInProgress())
+132 -10
View File
@@ -1093,6 +1093,29 @@ struct EventFilter
};
static QMap<QString, DrawFlags> DrawFlagsLookup;
static QStringList DrawFlagsList;
void CacheDrawFlagsLookup()
{
if(DrawFlagsLookup.empty())
{
for(uint32_t i = 0; i <= 31; i++)
{
DrawFlags flag = DrawFlags(1U << i);
// bit of a hack, see if it's a valid flag by stringising and seeing if it contains
// DrawFlags(
QString str = ToQStr(flag);
if(str.contains(lit("DrawFlags(")))
continue;
DrawFlagsList.push_back(str);
DrawFlagsLookup[str.toLower()] = flag;
}
DrawFlagsList.sort();
}
}
bool EvaluateFilterSet(ICaptureContext &ctx, const rdcarray<EventFilter> &filters, bool all,
uint32_t eid, const SDChunk *chunk, const DrawcallDescription *draw,
@@ -1187,11 +1210,13 @@ struct CustomFilterCallbacks
{
IEventBrowser::EventFilterCallback filter;
IEventBrowser::FilterParseCallback parser;
IEventBrowser::AutoCompleteCallback completer;
};
struct BuiltinFilterCallbacks
{
std::function<IEventBrowser::EventFilterCallback(QString, QString, ParseError &)> makeFilter;
IEventBrowser::AutoCompleteCallback completer;
};
struct EventFilterModel : public QSortFilterProxyModel
@@ -1220,6 +1245,19 @@ public:
MAKE_BUILTIN_FILTER(event);
MAKE_BUILTIN_FILTER(draw);
MAKE_BUILTIN_FILTER(dispatch);
m_BuiltinFilters[lit("event")].completer = [this](ICaptureContext *ctx, QString name,
QString parameters) {
return filterCompleter_event(ctx, name, parameters);
};
m_BuiltinFilters[lit("draw")].completer = [this](ICaptureContext *ctx, QString name,
QString parameters) {
return filterCompleter_draw(ctx, name, parameters);
};
m_BuiltinFilters[lit("dispatch")].completer = [this](ICaptureContext *ctx, QString name,
QString parameters) {
return filterCompleter_dispatch(ctx, name, parameters);
};
}
}
void ResetCache() { m_VisibleCache.clear(); }
@@ -1234,12 +1272,33 @@ public:
return ret;
}
QStringList GetCompletions(QString filter, QString params)
{
IEventBrowser::AutoCompleteCallback cb;
if(m_BuiltinFilters.contains(filter))
cb = m_BuiltinFilters[filter].completer;
else if(m_CustomFilters.contains(filter))
cb = m_CustomFilters[filter].completer;
rdcarray<rdcstr> ret;
if(cb)
ret = m_BuiltinFilters[filter].completer(&m_Ctx, filter, params);
QStringList qret;
for(const rdcstr &s : ret)
qret << s;
return qret;
}
QStringList GetBuiltinFunctions() { return m_BuiltinFilters.keys(); }
QStringList GetCustomFunctions() { return m_CustomFilters.keys(); }
void SetEmptyRegionsVisible(bool visible) { m_EmptyRegionsVisible = visible; }
static bool RegisterEventFilterFunction(const rdcstr &name,
IEventBrowser::EventFilterCallback filter,
IEventBrowser::FilterParseCallback parser)
IEventBrowser::FilterParseCallback parser,
IEventBrowser::AutoCompleteCallback completer)
{
if(m_BuiltinFilters.contains(name))
{
@@ -1255,7 +1314,7 @@ public:
return false;
}
m_CustomFilters[name] = {filter, parser};
m_CustomFilters[name] = {filter, parser, completer};
return true;
}
@@ -1477,6 +1536,15 @@ private:
};
}
rdcarray<rdcstr> filterCompleter_event(ICaptureContext *ctx, const rdcstr &name,
const rdcstr &params)
{
if(params.find_first_of(" \t") == -1)
return {"EID"};
return {};
}
IEventBrowser::EventFilterCallback filterFunction_event(QString name, QString parameters,
ParseError &errors)
{
@@ -1555,6 +1623,34 @@ private:
}
}
rdcarray<rdcstr> filterCompleter_draw(ICaptureContext *ctx, const rdcstr &name, const rdcstr &params)
{
if(params.find_first_of(" \t") == -1)
return {
"EID", "parent", "drawcallId", "numIndices",
// most aliases we don't autocomplete but this one we leave
"numVertices", "numInstances", "baseVertex", "indexOffset", "vertexOffset",
"instanceOffset", "dispatchX", "dispatchY", "dispatchZ", "dispatchSize", "duration",
"flags",
};
QList<Token> tokens = tokenise(params);
if(tokens[0].text == lit("flags") && tokens.size() >= 2)
{
CacheDrawFlagsLookup();
rdcarray<rdcstr> flags;
for(QString s : DrawFlagsList)
flags.push_back(s);
return flags;
}
return {};
}
IEventBrowser::EventFilterCallback filterFunction_draw(QString name, QString parameters,
ParseError &errors)
{
@@ -1585,10 +1681,11 @@ private:
static const NamedProp namedProps[] = {
NAMED_PROP("eventid", draw->eventId), NAMED_PROP("eid", draw->eventId),
NAMED_PROP("parent", draw->parent ? draw->parent->eventId : -12341234),
NAMED_PROP("drawcallid", draw->drawcallId), NAMED_PROP("numindices", draw->numIndices),
NAMED_PROP("numindexes", draw->numIndices), NAMED_PROP("numvertices", draw->numIndices),
NAMED_PROP("numvertexes", draw->numIndices), NAMED_PROP("indexcount", draw->numIndices),
NAMED_PROP("vertexcount", draw->numIndices), NAMED_PROP("numinstances", draw->numInstances),
NAMED_PROP("drawcallid", draw->drawcallId), NAMED_PROP("drawid", draw->drawcallId),
NAMED_PROP("numindices", draw->numIndices), NAMED_PROP("numindexes", draw->numIndices),
NAMED_PROP("numvertices", draw->numIndices), NAMED_PROP("numvertexes", draw->numIndices),
NAMED_PROP("indexcount", draw->numIndices), NAMED_PROP("vertexcount", draw->numIndices),
NAMED_PROP("numinstances", draw->numInstances),
NAMED_PROP("instancecount", draw->numInstances), NAMED_PROP("basevertex", draw->baseVertex),
NAMED_PROP("indexoffset", draw->indexOffset), NAMED_PROP("vertexoffset", draw->vertexOffset),
NAMED_PROP("instanceoffset", draw->instanceOffset),
@@ -1788,7 +1885,7 @@ private:
}
else if(tokens[0].text.toLower() == lit("flags"))
{
if(tokens.size() < 3 || tokens[1].text != lit("&"))
if(tokens.size() < 3 || (tokens[1].text != lit("&") && tokens[1].text != lit("=")))
{
errors.position = tokens[0].position;
errors.length = (tokens[1].position + tokens[1].length) - errors.position + 1;
@@ -1853,6 +1950,17 @@ private:
}
}
rdcarray<rdcstr> filterCompleter_dispatch(ICaptureContext *ctx, const rdcstr &name,
const rdcstr &params)
{
if(params.find_first_of(" \t") == -1)
return {
"EID", "eventId", "parent", "drawcallId", "drawId", "x", "y", "z", "size", "duration",
};
return {};
}
IEventBrowser::EventFilterCallback filterFunction_dispatch(QString name, QString parameters,
ParseError &errors)
{
@@ -1878,6 +1986,9 @@ private:
};
static const NamedProp namedProps[] = {
NAMED_PROP("eventid", draw->eventId), NAMED_PROP("eid", draw->eventId),
NAMED_PROP("parent", draw->parent ? draw->parent->eventId : -12341234),
NAMED_PROP("drawcallid", draw->drawcallId), NAMED_PROP("drawid", draw->drawcallId),
NAMED_PROP("dispatchx", draw->dispatchDimension[0]),
NAMED_PROP("dispatchy", draw->dispatchDimension[1]),
NAMED_PROP("dispatchz", draw->dispatchDimension[2]),
@@ -2712,6 +2823,15 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent)
ui->filterExpression->setCompletionStrings(completions);
}
else if(context.startsWith(QLatin1Char('$')) && context.contains(QLatin1Char('(')))
{
pos = context.indexOf(QLatin1Char('('));
QString filter = context.mid(1, pos - 1);
context.remove(0, pos + 1);
ui->filterExpression->setCompletionStrings(m_FilterModel->GetCompletions(filter, context));
}
else
{
ui->filterExpression->setCompletionStrings({});
@@ -3007,7 +3127,8 @@ void EventBrowser::on_filterExpression_keyPress(QKeyEvent *e)
filter_apply();
}
if(e->key() == Qt::Key_Dollar)
if(e->key() == Qt::Key_Dollar || e->key() == Qt::Key_Ampersand || e->key() == Qt::Key_Equal ||
e->key() == Qt::Key_Bar || e->key() == Qt::Key_ParenLeft)
{
// force autocompletion for filter functions, as long as we're not inside a quoted string
@@ -3625,9 +3746,10 @@ const DrawcallDescription *EventBrowser::GetDrawcallForEID(uint32_t eid)
}
bool EventBrowser::RegisterEventFilterFunction(const rdcstr &name, EventFilterCallback filter,
FilterParseCallback parser)
FilterParseCallback parser,
AutoCompleteCallback completer)
{
return EventFilterModel::RegisterEventFilterFunction(name, filter, parser);
return EventFilterModel::RegisterEventFilterFunction(name, filter, parser, completer);
}
bool EventBrowser::UnregisterEventFilterFunction(const rdcstr &name)
+2 -1
View File
@@ -81,7 +81,8 @@ public:
APIEvent GetAPIEventForEID(uint32_t eid) override;
const DrawcallDescription *GetDrawcallForEID(uint32_t eid) override;
bool RegisterEventFilterFunction(const rdcstr &name, EventFilterCallback filter,
FilterParseCallback parser) override;
FilterParseCallback parser,
AutoCompleteCallback completer) override;
bool UnregisterEventFilterFunction(const rdcstr &name) override;
void SetCurrentFilterText(const rdcstr &text) override;