如何为用户提供针对给定 boost::spirit 语法的高级自动完成建议?
How to provide user with advanced autocomplete suggestions for given boost::spirit grammar?
这实际上是在我之前的问题之后建立起来的:
对于 Sehe 解决方案,我添加了用于语法着色的注释器:
首先对这个采用hints
结构(注意:ast_type_t
是一个enum class
):
template <class ast_type_t> class hints_t
{
struct by_location_t
{
template<typename T, typename U> bool operator()(T const& a, U const& b) const
{
if(loc(a) == loc(b)) return size(a) > size(b);
return loc(a) < loc(b);
}
private:
static location_t loc(source_t const& s)
{
return s.begin();
}
static location_t loc(location_t const& l)
{
return l;
}
static std::size_t size(source_t const& s)
{
return s.end() - s.begin();
}
static std::size_t size(location_t const& s)
{
return 1;
}
};
public:
std::map<location_t, std::string, by_location_t> incomplete;
std::map<source_t, candidates_t, by_location_t> suggestions;
std::map<source_t, ast_type_t, by_location_t> annotations;
operator bool() const
{
return incomplete.size() || suggestions.size();
}
};
然后通过添加注释器本身:
struct annotation_t
{
typedef void result_type;
hints_t* hints;
template<typename first_t, typename last_t>
void operator()(ast::type_t id, first_t f, last_t l) const
{
if (hints)
{
source_t loc(&*f, l - f);
if (loc.size() > 0)
{
auto inserted = hints->annotations.emplace(loc, id);
if (!inserted.second) inserted.first->second = id;
}
}
}
};
::boost::phoenix::function<annotation_t> annotate{annotation_t{hints}};
最后将它们包含在解析器本身中:
on_success(lang_text, annotate(ast::type_t::lang_text, ::boost::spirit::qi::_1, ::boost::spirit::qi::_3));
这通常有效,但我遇到了另一个障碍;我需要自动完成功能,它实际上可以根据先前解析的值构建动态建议列表;最好的例子是使用 sehe 对原始问题的评论中的 foo.member1.member2
。我不允许在已解析的文本中定义成员,它们总是由外部 C++ 提供 class;
此类 C++ 的示例 class:
class member_provider_t
{
public: virtual ~member_provider_t() {}
public: virtual std::vector<std::string> possible_identifiers_after(...) = 0;
}
现在它变得棘手 - 我想将整个解析结果(到目前为止)传递给 ...
占位符(以解析的 ast 形式)。示例:解析自定义架构的 uri:
schema://main.menu.screen/show/ab... (suggest: about*)
(*about
- 这取决于到目前为止解析的整个 url)
如果这不可能,那么将尽可能多的已解析部分传递给提供程序函数的任何其他方法都适合我。
关于语法高亮,这些你见过吗?
关于上下文相关完成问题的第二部分,您只需要进行语义分析。这里要记住的最重要的事情是将解析和语义分析分开以简化您的生活。
在 中,我们已经有了一个能够表示部分正确输入的 AST。
这就是您所需要的。
从概念上讲,我会尝试将光标位置映射回相应的 AST 节点,并将其传递给完成引擎中的分析。当然,如果您的类型系统很大或范围规则使得构建符号表的成本很高,请考虑通过使用 AST 节点缓存符号表来进行优化。
关于复杂性的说明
当您尝试逐步更新该信息或避免在 "spurious states" 上丢失 "expensive" 状态时,事情会变得复杂(例如,如果用户仅输入平衡范围的开始部分,它会即使用户可能会立即通过关闭范围继续操作,破坏语义信息也是一种浪费。
请仔细计划您准备承担的复杂程度。没关系说"That's out of scope"。事实上,这比以你当时对我的第一反应的评论结束的结果要好:
Well, MS Intellisense also is getting lost on invalid code, 20% cases is still up there ;-) [...] – PiotrK
我坚持我的回答,我希望你意识到这基本上是相同的回答。如果您有 resources/resolve,您可以选择处理任意数量的复杂性。但始终确保您尝试查看您的开始,以及责任停止的地方。
这实际上是在我之前的问题之后建立起来的:
对于 Sehe 解决方案,我添加了用于语法着色的注释器:
首先对这个采用hints
结构(注意:ast_type_t
是一个enum class
):
template <class ast_type_t> class hints_t
{
struct by_location_t
{
template<typename T, typename U> bool operator()(T const& a, U const& b) const
{
if(loc(a) == loc(b)) return size(a) > size(b);
return loc(a) < loc(b);
}
private:
static location_t loc(source_t const& s)
{
return s.begin();
}
static location_t loc(location_t const& l)
{
return l;
}
static std::size_t size(source_t const& s)
{
return s.end() - s.begin();
}
static std::size_t size(location_t const& s)
{
return 1;
}
};
public:
std::map<location_t, std::string, by_location_t> incomplete;
std::map<source_t, candidates_t, by_location_t> suggestions;
std::map<source_t, ast_type_t, by_location_t> annotations;
operator bool() const
{
return incomplete.size() || suggestions.size();
}
};
然后通过添加注释器本身:
struct annotation_t
{
typedef void result_type;
hints_t* hints;
template<typename first_t, typename last_t>
void operator()(ast::type_t id, first_t f, last_t l) const
{
if (hints)
{
source_t loc(&*f, l - f);
if (loc.size() > 0)
{
auto inserted = hints->annotations.emplace(loc, id);
if (!inserted.second) inserted.first->second = id;
}
}
}
};
::boost::phoenix::function<annotation_t> annotate{annotation_t{hints}};
最后将它们包含在解析器本身中:
on_success(lang_text, annotate(ast::type_t::lang_text, ::boost::spirit::qi::_1, ::boost::spirit::qi::_3));
这通常有效,但我遇到了另一个障碍;我需要自动完成功能,它实际上可以根据先前解析的值构建动态建议列表;最好的例子是使用 sehe 对原始问题的评论中的 foo.member1.member2
。我不允许在已解析的文本中定义成员,它们总是由外部 C++ 提供 class;
此类 C++ 的示例 class:
class member_provider_t
{
public: virtual ~member_provider_t() {}
public: virtual std::vector<std::string> possible_identifiers_after(...) = 0;
}
现在它变得棘手 - 我想将整个解析结果(到目前为止)传递给 ...
占位符(以解析的 ast 形式)。示例:解析自定义架构的 uri:
schema://main.menu.screen/show/ab... (suggest: about*)
(*about
- 这取决于到目前为止解析的整个 url)
如果这不可能,那么将尽可能多的已解析部分传递给提供程序函数的任何其他方法都适合我。
关于语法高亮,这些你见过吗?
关于上下文相关完成问题的第二部分,您只需要进行语义分析。这里要记住的最重要的事情是将解析和语义分析分开以简化您的生活。
在
从概念上讲,我会尝试将光标位置映射回相应的 AST 节点,并将其传递给完成引擎中的分析。当然,如果您的类型系统很大或范围规则使得构建符号表的成本很高,请考虑通过使用 AST 节点缓存符号表来进行优化。
关于复杂性的说明
当您尝试逐步更新该信息或避免在 "spurious states" 上丢失 "expensive" 状态时,事情会变得复杂(例如,如果用户仅输入平衡范围的开始部分,它会即使用户可能会立即通过关闭范围继续操作,破坏语义信息也是一种浪费。
请仔细计划您准备承担的复杂程度。没关系说"That's out of scope"。事实上,这比以你当时对我的第一反应的评论结束的结果要好:
Well, MS Intellisense also is getting lost on invalid code, 20% cases is still up there ;-) [...] – PiotrK
我坚持我的回答,我希望你意识到这基本上是相同的回答。如果您有 resources/resolve,您可以选择处理任意数量的复杂性。但始终确保您尝试查看您的开始,以及责任停止的地方。