Initial, WIP spelling implementation

Add a ranges highlighter that takes a timestamped list of ranges and
associated face. Add a spell.kak file that uses aspell pipe interface
to fill a range-faces option.
This commit is contained in:
Maxime Coste 2015-12-17 04:07:09 +00:00
parent 925d41f596
commit 8dcffd8f5a
6 changed files with 122 additions and 1 deletions

40
rc/spell.kak Normal file
View File

@ -0,0 +1,40 @@
decl -hidden range-faces spell_regions
decl -hidden str spell_tmp_file
def spell %{
try %{ addhl ranges 'spell_regions' }
%sh{
file=$(mktemp -d -t kak-spell.XXXXXXXX)/buffer
echo "write ${file}"
echo "set buffer spell_tmp_file ${file}"
}
%sh{
sed -ie 's/^/^/' $kak_opt_spell_tmp_file
aspell -a < $kak_opt_spell_tmp_file 2>&1 | {
line_num=1
regions=$kak_timestamp
while read line; do
case $line in
\&*)
word=$(echo "$line" | cut -d ' ' -f 2)
begin=$(echo "$line" | cut -d ' ' -f 4 | sed 's/:$//')
end=$((begin + ${#word}))
# echo "echo -debug -- line: $line_num, word: $word, begin: $begin, end: $end"
regions="$regions:$line_num.$begin,$line_num.$end|Error"
;;
'#'*)
word=$(echo "$line" | cut -d ' ' -f 2)
begin=$(echo "$line" | cut -d ' ' -f 3)
end=$((begin + ${#word}))
# echo "echo -debug -- line: $line_num, word: $word, begin: $begin, end: $end"
regions="$regions:$line_num.$begin,$line_num.$end|Error"
;;
'') ((++line_num)) ;;
*) ;;
esac
done
echo "set buffer spell_regions %{$regions}"
}
rm -r $(dirname $kak_opt_spell_tmp_file)
}
}

View File

@ -1104,7 +1104,8 @@ const CommandDesc declare_option_cmd = {
" regex: regular expression\n" " regex: regular expression\n"
" int-list: list of integers\n" " int-list: list of integers\n"
" str-list: list of character strings\n" " str-list: list of character strings\n"
" line-flags: list of line flags\n", " line-flags: list of line flags\n"
" range-faces: list of range faces\n",
ParameterDesc{ ParameterDesc{
{ { "hidden", { false, "do not display option name when completing" } }, { { "hidden", { false, "do not display option name when completing" } },
{ "docstring", { true, "specify option description" } } }, { "docstring", { true, "specify option description" } } },
@ -1138,6 +1139,8 @@ const CommandDesc declare_option_cmd = {
opt = &reg.declare_option<Vector<String, MemoryDomain::Options>>(parser[1], docstring, {}, flags); opt = &reg.declare_option<Vector<String, MemoryDomain::Options>>(parser[1], docstring, {}, flags);
else if (parser[0] == "line-flags") else if (parser[0] == "line-flags")
opt = &reg.declare_option<TimestampedList<LineAndFlag>>(parser[1], docstring, {}, flags); opt = &reg.declare_option<TimestampedList<LineAndFlag>>(parser[1], docstring, {}, flags);
else if (parser[0] == "range-faces")
opt = &reg.declare_option<TimestampedList<RangeAndFace>>(parser[1], docstring, {}, flags);
else else
throw runtime_error(format("unknown type {}", parser[0])); throw runtime_error(format("unknown type {}", parser[0]));

View File

@ -9,6 +9,33 @@
namespace Kakoune namespace Kakoune
{ {
String option_to_string(BufferRange range)
{
return format("{}.{},{}.{}",
range.begin.line+1, range.begin.column+1,
range.end.line+1, range.end.column+1);
}
void option_from_string(StringView str, BufferRange& opt)
{
auto comma = find(str, ',');
auto dot_begin = find(StringView{str.begin(), comma}, '.');
auto dot_end = find(StringView{comma, str.end()}, '.');
if (comma == str.end() or dot_begin == comma or dot_end == str.end())
throw runtime_error(format("'{}' does not follow <line>.<column>,<line>.<column> format", str));
ByteCoord begin{str_to_int({str.begin(), dot_begin}) - 1,
str_to_int({dot_begin+1, comma}) - 1};
ByteCoord end{str_to_int({comma+1, dot_end}) - 1,
str_to_int({dot_end+1, str.end()}) - 1};
opt.begin = begin;
opt.end = end;
}
StringView DisplayAtom::content() const StringView DisplayAtom::content() const
{ {
switch (m_type) switch (m_type)

View File

@ -13,6 +13,9 @@ namespace Kakoune
class Buffer; class Buffer;
struct BufferRange{ ByteCoord begin, end; }; struct BufferRange{ ByteCoord begin, end; };
String option_to_string(BufferRange range);
void option_from_string(StringView str, BufferRange& opt);
inline bool operator==(const BufferRange& lhs, const BufferRange& rhs) inline bool operator==(const BufferRange& lhs, const BufferRange& rhs)
{ {
return lhs.begin == rhs.begin and lhs.end == rhs.end; return lhs.begin == rhs.begin and lhs.end == rhs.end;

View File

@ -982,6 +982,48 @@ HighlighterAndId create_flag_lines_highlighter(HighlighterParameters params)
return {"hlflags_" + params[1], make_simple_highlighter(func) }; return {"hlflags_" + params[1], make_simple_highlighter(func) };
} }
HighlighterAndId create_ranges_highlighter(HighlighterParameters params)
{
if (params.size() != 1)
throw runtime_error("wrong parameter count");
const String& option_name = params[0];
// throw if wrong option type
GlobalScope::instance().options()[option_name].get<TimestampedList<RangeAndFace>>();
auto func = [=](const Context& context, HighlightFlags flags,
DisplayBuffer& display_buffer, BufferRange)
{
auto& range_and_faces = context.options()[option_name].get_mutable<TimestampedList<RangeAndFace>>();
auto& ranges = range_and_faces.list;
auto& buffer = context.buffer();
if (range_and_faces.timestamp != buffer.timestamp())
{
// TODO: update ranges to current timestamp
return;
}
for (auto& range : ranges)
{
try
{
auto& r = std::get<0>(range);
if (not buffer.is_valid(r.begin) or not buffer.is_valid(r.end))
continue;
highlight_range(display_buffer, r.begin, r.end, true,
apply_face(get_face(std::get<1>(range))));
}
catch (runtime_error&)
{}
}
};
return {"hlranges_" + params[1], make_simple_highlighter(func) };
}
HighlighterAndId create_highlighter_group(HighlighterParameters params) HighlighterAndId create_highlighter_group(HighlighterParameters params)
{ {
if (params.size() != 1) if (params.size() != 1)
@ -1441,6 +1483,11 @@ void register_highlighters()
"Parameters: <option name> <bg color>\n" "Parameters: <option name> <bg color>\n"
"Display flags specified in the line-flag-list option <option name>\n" "Display flags specified in the line-flag-list option <option name>\n"
"A line-flag is written: <line>|<fg color>|<text>, the list is : separated" } }); "A line-flag is written: <line>|<fg color>|<text>, the list is : separated" } });
registry.append({
"ranges",
{ create_ranges_highlighter,
"Parameters: <option name>\n"
"Use the range-faces option given as parameter to highlight buffer\n" } });
registry.append({ registry.append({
"line", "line",
{ create_line_highlighter, { create_line_highlighter,

View File

@ -10,6 +10,7 @@ namespace Kakoune
void register_highlighters(); void register_highlighters();
using LineAndFlag = std::tuple<LineCount, String>; using LineAndFlag = std::tuple<LineCount, String>;
using RangeAndFace = std::tuple<BufferRange, String>;
} }