From 246a32797a17e7cdebcde64ac98c09501d1da10d Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 12 Oct 2020 12:14:01 +1100 Subject: [PATCH 1/4] Fix region regexes incorrectly matching ^$ at end of line Because no flags were set for regex matching, the regex engine was assuming that the subject string past-the-end matched a end-of-line. As the subject string already ended with a \n character, the regex engine processing of the "past-the-end" position would match '^$' as ^ matched past the existing \n and $ matched the assumed end-of-line. Fixes #3799 --- src/highlighters.cc | 3 ++- test/regression/3799-incorrect-region-match/cmd | 1 + test/regression/3799-incorrect-region-match/in | 4 ++++ test/regression/3799-incorrect-region-match/rc | 2 ++ test/regression/3799-incorrect-region-match/script | 2 ++ 5 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 test/regression/3799-incorrect-region-match/cmd create mode 100644 test/regression/3799-incorrect-region-match/in create mode 100644 test/regression/3799-incorrect-region-match/rc create mode 100644 test/regression/3799-incorrect-region-match/script diff --git a/src/highlighters.cc b/src/highlighters.cc index 78099b08..309ade94 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -1744,7 +1744,8 @@ void insert_matches(const Buffer& buffer, RegexMatchList& matches, const Regex& for (auto line = range.begin; line < range.end; ++line) { const StringView l = buffer[line]; - for (auto&& m : RegexIterator{l.begin(), l.end(), vm}) + const auto flags = RegexExecFlags::NotEndOfLine; // buffer line already ends with \n + for (auto&& m : RegexIterator{l.begin(), l.end(), vm, flags}) { const bool with_capture = capture and m[1].matched and m[0].second - m[0].first < std::numeric_limits::max(); diff --git a/test/regression/3799-incorrect-region-match/cmd b/test/regression/3799-incorrect-region-match/cmd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/regression/3799-incorrect-region-match/cmd @@ -0,0 +1 @@ + diff --git a/test/regression/3799-incorrect-region-match/in b/test/regression/3799-incorrect-region-match/in new file mode 100644 index 00000000..0956e089 --- /dev/null +++ b/test/regression/3799-incorrect-region-match/in @@ -0,0 +1,4 @@ +print << ""; +part of heredoc + +not part of heredoc diff --git a/test/regression/3799-incorrect-region-match/rc b/test/regression/3799-incorrect-region-match/rc new file mode 100644 index 00000000..3e1998b7 --- /dev/null +++ b/test/regression/3799-incorrect-region-match/rc @@ -0,0 +1,2 @@ +add-highlighter global/regions regions +add-highlighter global/regions/heredoc region -match-capture <<\h*"(\w*)" ^(\w*)$ fill red diff --git a/test/regression/3799-incorrect-region-match/script b/test/regression/3799-incorrect-region-match/script new file mode 100644 index 00000000..ec4075b4 --- /dev/null +++ b/test/regression/3799-incorrect-region-match/script @@ -0,0 +1,2 @@ +ui_out '{ "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] }' +ui_out '{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "p" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "rint " }, { "face": { "fg": "red", "bg": "default", "attributes": [] }, "contents": "<< \"\";\u000a" }], [{ "face": { "fg": "red", "bg": "default", "attributes": [] }, "contents": "part of heredoc\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "not part of heredoc\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }' From f6c67f783185e649b42db26656ab588b48084809 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 12 Oct 2020 12:41:52 +1100 Subject: [PATCH 2/4] Code cleanup in insert completer --- src/insert_completer.cc | 126 ++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/src/insert_completer.cc b/src/insert_completer.cc index 94c1a95e..03ce45a5 100644 --- a/src/insert_completer.cc +++ b/src/insert_completer.cc @@ -261,76 +261,74 @@ InsertCompletion complete_option(const SelectionList& sels, auto& desc = opt.prefix; static const Regex re(R"((\d+)\.(\d+)(?:\+(\d+))?@(\d+))"); MatchResults match; - if (regex_match(desc.begin(), desc.end(), match, re)) + if (not regex_match(desc.begin(), desc.end(), match, re)) + return {}; + + BufferCoord coord{str_to_int({match[1].first, match[1].second}) - 1, + str_to_int({match[2].first, match[2].second}) - 1}; + if (not buffer.is_valid(coord)) + return {}; + size_t timestamp = (size_t)str_to_int({match[4].first, match[4].second}); + auto changes = buffer.changes_since(timestamp); + if (any_of(changes, [&](auto&& change) { return change.begin < coord; })) + return {}; + + if (cursor_pos.line != coord.line or cursor_pos.column < coord.column) + return {}; + + const ColumnCount tabstop = options["tabstop"].get(); + const ColumnCount column = get_column(buffer, tabstop, cursor_pos); + + struct RankedMatchAndInfo : RankedMatch { - BufferCoord coord{ str_to_int({match[1].first, match[1].second}) - 1, - str_to_int({match[2].first, match[2].second}) - 1 }; - if (not buffer.is_valid(coord)) - return {}; - size_t timestamp = (size_t)str_to_int({match[4].first, match[4].second}); - auto changes = buffer.changes_since(timestamp); - if (any_of(changes, [&](auto&& change) { return change.begin < coord; })) - return {}; + using RankedMatch::RankedMatch; + using RankedMatch::operator==; + using RankedMatch::operator<; - if (cursor_pos.line == coord.line and cursor_pos.column >= coord.column) + StringView on_select; + DisplayLine menu_entry; + }; + + StringView query = buffer.substr(coord, cursor_pos); + Vector matches; + + for (auto& candidate : opt.list) + { + if (RankedMatchAndInfo match{std::get<0>(candidate), query}) { - StringView query = buffer.substr(coord, cursor_pos); + match.on_select = std::get<1>(candidate); + auto& menu = std::get<2>(candidate); + match.menu_entry = not menu.empty() ? + parse_display_line(expand_tabs(menu, tabstop, column), faces) + : DisplayLine{String{}, {}}; - const ColumnCount tabstop = options["tabstop"].get(); - const ColumnCount column = get_column(buffer, tabstop, cursor_pos); - - struct RankedMatchAndInfo : RankedMatch - { - using RankedMatch::RankedMatch; - using RankedMatch::operator==; - using RankedMatch::operator<; - - StringView on_select; - DisplayLine menu_entry; - }; - - Vector matches; - - for (auto& candidate : opt.list) - { - if (RankedMatchAndInfo match{std::get<0>(candidate), query}) - { - match.on_select = std::get<1>(candidate); - auto& menu = std::get<2>(candidate); - match.menu_entry = not menu.empty() ? - parse_display_line(expand_tabs(menu, tabstop, column), faces) - : DisplayLine{ expand_tabs(menu, tabstop, column), {} }; - - matches.push_back(std::move(match)); - } - } - - constexpr size_t max_count = 100; - // Gather best max_count matches - auto greater = [](auto& lhs, auto& rhs) { return rhs < lhs; }; - auto first = matches.begin(), last = matches.end(); - std::make_heap(first, last, greater); - InsertCompletion::CandidateList candidates; - candidates.reserve(std::min(matches.size(), max_count)); - candidates.reserve(matches.size()); - while (candidates.size() < max_count and first != last) - { - if (candidates.empty() or candidates.back().completion != first->candidate()) - candidates.push_back({ first->candidate().str(), first->on_select.str(), - std::move(first->menu_entry) }); - std::pop_heap(first, last--, greater); - } - - auto end = cursor_pos; - if (match[3].matched) - { - ByteCount len = str_to_int({match[3].first, match[3].second}); - end = buffer.advance(coord, len); - } - return { std::move(candidates), coord, end, timestamp }; + matches.push_back(std::move(match)); } } - return {}; + + constexpr size_t max_count = 100; + // Gather best max_count matches + auto greater = [](auto& lhs, auto& rhs) { return rhs < lhs; }; + auto first = matches.begin(), last = matches.end(); + std::make_heap(first, last, greater); + InsertCompletion::CandidateList candidates; + candidates.reserve(std::min(matches.size(), max_count)); + candidates.reserve(matches.size()); + while (candidates.size() < max_count and first != last) + { + if (candidates.empty() or candidates.back().completion != first->candidate()) + candidates.push_back({ first->candidate().str(), first->on_select.str(), + std::move(first->menu_entry) }); + std::pop_heap(first, last--, greater); + } + + auto end = cursor_pos; + if (match[3].matched) + { + ByteCount len = str_to_int({match[3].first, match[3].second}); + end = buffer.advance(coord, len); + } + return { std::move(candidates), coord, end, timestamp }; } template From 1de058cdf8d5aadbe617aa39f20c9c959e3db269 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 12 Oct 2020 13:04:08 +1100 Subject: [PATCH 3/4] Fix rust highlighting after regions highlighter behaviour change --- rc/filetype/rust.kak | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rc/filetype/rust.kak b/rc/filetype/rust.kak index 8588789e..3db474d7 100644 --- a/rc/filetype/rust.kak +++ b/rc/filetype/rust.kak @@ -36,10 +36,10 @@ add-highlighter shared/rust/string region %{(? Date: Sun, 31 May 2020 10:17:36 +0200 Subject: [PATCH 4/4] Add support for VHDL filetype. --- rc/filetype/vhdl.kak | 408 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 rc/filetype/vhdl.kak diff --git a/rc/filetype/vhdl.kak b/rc/filetype/vhdl.kak new file mode 100644 index 00000000..93108d2f --- /dev/null +++ b/rc/filetype/vhdl.kak @@ -0,0 +1,408 @@ +# Based on IEEE Std 1076‐2019 + +# Detection +hook global BufCreate .*[.](vhd[l]?) %[ + set-option buffer filetype vhdl +] + +# Initialization +hook global WinSetOption filetype=vhdl %[ + require-module vhdl + set-option window static_words %opt{vhdl_static_words} + hook -group vhdl-indent window InsertChar \n vhdl-indent-on-new-line + hook -group vhdl-indent window InsertChar \) vhdl-indent-on-closing-parenthesis + hook -group vhdl-insert window InsertChar \n vhdl-insert-on-new-line + # Cleanup trailing whitespaces on current line insert end. + hook -group vhdl-trim-indent window ModeChange pop:insert:.* %[ try %[ execute-keys -draft s ^\h+$ d ] ] + hook -once -always window WinSetOption filetype=.* %[ remove-hooks window vhdl-.+ ] +] + +hook -group vhdl-highlight global WinSetOption filetype=vhdl %[ + add-highlighter window/vhdl ref vhdl + hook -once -always window WinSetOption filetype=.* %[ remove-highlighter window/vhdl ] +] + +provide-module vhdl %§ + +# Highlighters & Completion +add-highlighter shared/vhdl regions +add-highlighter shared/vhdl/code default-region group +add-highlighter shared/vhdl/comment_line region '--' $ fill comment +add-highlighter shared/vhdl/comment region /\* \*/ fill comment + +# Integer formats +add-highlighter shared/vhdl/code/ regex '(?i)\b0b[01]+l?\b' 0:value +add-highlighter shared/vhdl/code/ regex '(?i)\b0x[\da-f]+l?\b' 0:value +add-highlighter shared/vhdl/code/ regex '(?i)\b0o?[0-7]+l?\b' 0:value +add-highlighter shared/vhdl/code/ regex '(?i)\b([1-9]\d*|0)l?\b' 0:value +# Float formats +add-highlighter shared/vhdl/code/ regex '\b\d+[eE][+-]?\d+\b' 0:value +add-highlighter shared/vhdl/code/ regex '(\b\d+)?\.\d+\b' 0:value +add-highlighter shared/vhdl/code/ regex '\b\d+\.' 0:value +# Imaginary formats +add-highlighter shared/vhdl/code/ regex '\b\d+\+\d+[jJ]\b' 0:value + +evaluate-commands %sh[ + values="true false note warning error failure" + + # LRM 5.2.4.1 + units="fs ps ns us ms sec min Å nm um mm cm m km" + + # LRM 16.2 + predefined_attributes=" + base left right high low ascending length range reverse_range + subtype image pos succ pred leftof rightof value val + designated_subtype reflect high low index element delayed + stable quiet transaction event active last_event last_active + last_value driving driving_value simple_name instance_name + path_name record signal converse + " + + libraries="ieee std" + + packages=" + math_real math_complex std_logic_1164 std_logic_textio numeric_bit numeric_std + numeric_bit_unsigned numeric_std_unsigned fixed_float_types fixed_generic_pkg + fixed_pkg float_generic_pkg float_pkg + standard textio env + " + + # LRM 15.10 + reserved_words=" + abs access after alias all and architecture array assert assume assume_guarantee attribute + begin block body buffer bus + case component configuration constant context cover + default disconnect downto + else elsif end entity exit + fairness file for force function + generate generic group guarded + if impure in inertial inout is + label library linkage literal loop + map mod + nand new next nor not null + of on open or others out + package parameter port postponed procedure process property protected pure + range record register reject release rem report restrict restrict_guarantee return rol ror + select sequence severity signal shared sla sll sra srl strong subtype + then to transport type + unaffected units until use + variable view vpkg vmode vprop vunit + wait when while with + xnor xor + " + + types=" + bit bit_vector + boolean + file_open_state file_origin_kind + integer natural positive + line line_vector + std_logic std_logic_vector + std_ulogic std_ulogic_vector + side + signed unsigned + string text + time + " + + functions=" + find_leftmost find_rightmost divide reciprocal remainder modulo minimum maximum + std_match add_carry scalb + resize to_ufixed to_sfixed to_unsigned to_signed to_real to_integer to_slv + to_std_logic_vector to_stdlogicvector to_sulv to_std_ulogic_vector to_std_ulogicvector + to_01 is_x to_x01 to_ux01 to_x01z + ufixed_high ufixed_low sfixed_high sfixed_low to_ufix to_sfix ufix_high ufix_low + sfix_high sfix_low + write read bwrite binary_write bread binary_read owrite oread octal_write octal_read + hwrite hread hex_write hex_read to_string to_bstring to_binary_string to_ostring + to_octal_string to_hstring to_hex_string from_string from_bstring from_binary_string + from_ostring from_octal_string from_hstring from_hex_string + rising_edge falling_edge + " + + join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; } + + # Add the language's grammar to the static completion list + printf %s\\n "declare-option str-list vhdl_static_words $(join "${values} ${units} ${predefined_attributes} ${libraries} ${packages} ${reserved_words} ${types} ${functions}" ' ')" + + # Highlight keywords + printf %s " + add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${values}" '|'))\b' 0:value + add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${units}" '|'))\b' 0:meta + add-highlighter shared/vhdl/code/ regex \"'(?i)\b($(join "${predefined_attributes}" '|'))\b\" 0:attribute + add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${libraries}" '|'))\b' 0:builtin + add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${packages}" '|'))\b' 0:builtin + add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${reserved_words}" '|'))\b' 0:keyword + add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${functions}" '|'))\b\(' 1:builtin + add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${types}" '|'))\b' 0:type + add-highlighter shared/vhdl/code/ regex '^\h*(@[\w_.]+))' 1:attribute + " +] + +add-highlighter shared/vhdl/code/ regex \(|\)|\;|\.|,|:|\| 0:attribute + +add-highlighter shared/vhdl/code/ regex \?\?|=|/=|<|<=|>|>=|\?=|\?/=|\?<|\?<=|\?>|\?>=|\+|-|&|\*|/|:= 0:operator + +# Meta values highlight. +# The values 'U', 'X', 'W', and '–' are metalogical values; they define the behavior of the model itself rather than the behavior of the hardware being synthesized. +add-highlighter shared/vhdl/code/ regex "(?i)'[U|X|W|-]'" 0:meta +# Highlight other logical values. +add-highlighter shared/vhdl/code/ regex "(?i)'[0|1|Z|L|H]'" 0:value + +# String +add-highlighter shared/vhdl/code/ regex '"[^"]*"' 0:string + +# Binary vector. +add-highlighter shared/vhdl/code/ regex '[bB]"[01_]*"' 0:value + +# Octal vector. +add-highlighter shared/vhdl/code/ regex '[oO]"[01234567_]*"' 0:value + +# Hex vector. +add-highlighter shared/vhdl/code/ regex '(?i)x"[0123456789abcdef_]*"' 0:value + +define-command -hidden vhdl-insert-on-new-line %[ + # Handle comment lines. + evaluate-commands -itersel %[ + # Copy '--' comment prefix and following white spaces. + try %[ + # is needed because of "Preserve previous line indent" command. + try %[ execute-keys -draft k s ^\h*--\h* y j gh P ] + ] + ] + + evaluate-commands -save-regs x %[ + # Save previous line indent in register x. + try %[ execute-keys -draft ks^\h+"xy ] catch %[ reg x '' ] + + # All "wisely add" commands share the same concept. + # Only "end if" has extra comments. + # Wisely add "end if;". + evaluate-commands %[ + try %[ + # Validate previous line and that it is not closed yet. + execute-keys -draft k ^\h*(?i)((then|(.*:\h*)?if\b.*\bthen)$) J}iJ ^x(?i)(else|end|elsif)\b + # Don't add for "if ... generate", it requires "end generate;". + execute-keys -draft k (?i)\bgenerate\b + execute-keys -draft oxendif + ] + ] + # Wisely add "end generate;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i).*\bgenerate$ J}iJ ^x(?i)(begin|end) + # Don't add in case of comment line. + execute-keys -draft k ^\h*-- + execute-keys -draft oxendgenerate + ] + ] + # Wisely add "end case;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)(case|.*\h*:\h*case)\b J}iJ ^x(?i)end + execute-keys -draft oxendcase + ] + ] + # Wisely add "begin" and "end block;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)((block|.*:\h*block)\b) J}iJ ^x(?i)(begin|end) + execute-keys -draft oxbeginxendblock + ] + ] + # Wisely add "begin" and "end process;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)(.*:\h*)?(postponed\h+)?process\b J}iJ ^x(?i)(begin|end) + execute-keys -draft oxbeginxendprocess + ] + ] + # Wisely add "end loop;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)(.*\bloop|.*\h*:\h*(for|loop))$ J}iJ ^x(?i)(end) + execute-keys -draft oxendloop + ] + ] + # Wisely add "end protected;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)(type\b.*\bis\h+protected)$ J}iJ ^x(?i)(end) + execute-keys -draft oxendprotected + ] + ] + # Wisely add "end record;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)(type\b.*\bis\h+record\h*)$ J}iJ ^x(?i)(end) + execute-keys -draft oxendrecord + ] + ] + # Wisely add ");" for "type ... is (". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)(type\b.*\bis\h+\(\h*)$ J}iJ ^x(\)) + execute-keys -draft ox) + ] + ] + # Wisely add "end entity;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^(?i)\h*entity\b.*\bis$ J}iJ ^x(?i)(begin|end) + execute-keys -draft oxendentity + ] + ] + # Wisely add "begin" and "end function;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^(?i)(\h*\)?\h*return\b.*\bis$) J}iJ ^x(?i)(begin|end) + execute-keys -draft oxbeginxendfunction + ] + try %[ + execute-keys -draft k ^(?i)(\h*((pure|impure)\h+)?function\b.*\bis$) J}iJ ^x(?i)(begin|end) + execute-keys -draft oxbeginxendfunction + ] + ] + # Wisely add "begin" and "end procedure;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^(?i)(\h*procedure\b.*\bis$) J}iJ ^x(?i)\b(begin|end)\b + execute-keys -draft oxbeginxendprocedure + ] + try %[ + execute-keys -draft k ^(?i)\h*\)\h*\bis$ J}iJ ^x(?i)\b(begin|end)\b + # Verify that line with opening parenthesis contains "procedure" keyword. + execute-keys -draft k s\) (?i)\bprocedure\b + execute-keys -draft oxbeginxendprocedure + ] + ] + # Wisely add "end package;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^(?i)(package\b) J}iJ ^x(?i)(end) + # Make sure it is not package body. + execute-keys -draft k(?i)\bbody\b + execute-keys -draft oendpackage + ] + ] + # Wisely add "end package body;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^(?i)(package\h+body\b) J}iJ ^x(?i)(end) + execute-keys -draft oendpackagebody + ] + ] + # Wisely add "begin" and "end architecture;". + evaluate-commands %[ + try %[ + execute-keys -draft k ^(?i)\h*architecture\b J}iJ ^x(?i)(begin|end) + execute-keys -draft oxbeginxendarchitecture + ] + ] + # Wisely add ");" for "port (". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)port\h*\($ J}iJ ^x(\)\;) + execute-keys -draft ox) + ] + ] + # Wisely add ");" for "port map (". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)port\h+map\h*\($ J}iJ ^x(\)\;) + execute-keys -draft ox) + ] + ] + # Wisely add ");" for "generic (". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)generic\h*\($ J}iJ ^x(\)\;) + execute-keys -draft ox) + ] + ] + # Wisely add ")" for "generic map (". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)generic\h+map\h*\($ J}iJ ^x(\)) + execute-keys -draft ox) + ] + ] + # Wisely add ") return ;" for "[pure|impure] function ... (". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)(pure\b|impure\b)?\h*function\b.*\h*\($ J}iJ ^x(\)\h*return.*) + execute-keys -draft ox)return + ] + ] + # Wisely add ");" for "procedure ... (". + evaluate-commands %[ + try %[ + execute-keys -draft k ^\h*(?i)procedure\b.*\h*\($ J}iJ ^x(\)\h*\;) + execute-keys -draft ox) + ] + ] + ] +] + +define-command -hidden vhdl-indent-on-new-line %{ + evaluate-commands -itersel %{ + # Align "then" to previous "if|elsif". + evaluate-commands -itersel -save-regs x %[ + try %[ + execute-keys -draft k (?i)^\h*then$ + try %[ execute-keys -draft (?i)\b(if|elsif)\bs^\h+"xy ] catch %[ reg x '' ] + try %[ execute-keys -draft k s^\h+d ] catch %[ ] + execute-keys -draft kgh ix + ] + ] + + # Align "generate" to previous "if|for". + evaluate-commands -itersel -save-regs x %[ + try %[ + execute-keys -draft k (?i)^\h*generate$ + try %[ execute-keys -draft (?i)\b(if|for)\bs^\h+"xy ] catch %[ reg x '' ] + try %[ execute-keys -draft k s^\h+d ] catch %[ ] + execute-keys -draft kgh ix + ] + ] + + # Preserve previous line indent. + try %[ execute-keys -draft K ] + + # Cleanup trailing whitespaces from previous line. + try %[ execute-keys -draft k s \h+$ d ] + + # Increase indent after some keywords. + try %[ + execute-keys -draft k (?i)\b(begin|block|else|for|generate|if|is|loop|process|protected|record|select|then)$ + # Does not indent if in comment line. + execute-keys -draft k(?i)^\h*-- + # Handle case line in a bit different way. + execute-keys -draft k(?i)^\h*case\b + execute-keys -draft + ] + + # Add "when " and increase indent after "case ... is". + try %[ + execute-keys -draft k (?i)\h*case\b.*\h+is$ + # Don't indent if in comment line. + execute-keys -draft k(?i)^\h*-- + execute-keys -draft iwhen + ] + + # Copy the indentation of the matching if. + try %{ execute-keys -draft k ^\h*(elsif\b|else$) gh [c^\h*(\S*\h*:\h*)?if\b,\bend\sif\b 1 j K } + + # Increase indent after some operators. + try %[ execute-keys -draft k (\(|=>|<=|:=)$ j ] + } +} + +define-command vhdl-indent-on-closing-parenthesis %[ + evaluate-commands -itersel %[ + # Decrease indent after ")" at the beginning of line. + try %[ execute-keys -draft (^\h+\)$) ] + ] +] + +§