mawww / kakoune

mawww's experiment for a better code editor
http://kakoune.org
The Unlicense
9.87k stars 712 forks source link

Spell checking only comments #2581

Open dpc opened 5 years ago

dpc commented 5 years ago

Spell-checking in code is not very practical, except for comments.

If :spell could be made that it only check the current selection, then maybe it would be possible to have a little plugin that selects only comments, and then calls :spell on such selection. Just a thought.

mawww commented 5 years ago

I agree we should have a :spell-selection that would only spell check the selection (and :spell would just select the whole buffer and run that).

lenormf commented 5 years ago

I got an implementation going, but the issue is that aspell returns coordinates within a selection, and adding the right offsets to align those coordinates within the buffer space is tricky.

diff --git a/rc/base/spell.kak b/rc/base/spell.kak
index 10c0f915..574d2e39 100644
--- a/rc/base/spell.kak
+++ b/rc/base/spell.kak
@@ -1,25 +1,26 @@
 declare-option -hidden range-specs spell_regions
 declare-option -hidden str spell_lang
-declare-option -hidden str spell_tmp_file
+declare-option -hidden str spell_tmp_dir
+
+define-command -params ..1 -docstring %{
+spell-selection [<language>]: spell-check the current selection
+The first optional argument is the language against which the check will be performed, as described by the `:spell` command
+    } spell-selection %{
+    set-option window spell_regions %val{timestamp}
+    try %{ add-highlighter window/ ranges spell_regions }

-define-command -params ..1 \
-    -docstring %{spell [<language>]: spell check the current buffer
-The first optional argument is the language against which the check will be performed
-Formats of language supported:
- - ISO language code, e.g. 'en'
- - language code above followed by a dash or underscore with an ISO country code, e.g. 'en-US'} \
-    spell %{
-    try %{ add-highlighter window/ ranges 'spell_regions' }
     evaluate-commands %sh{
-        file=$(mktemp -d "${TMPDIR:-/tmp}"/kak-spell.XXXXXXXX)/buffer
-        printf 'eval -no-hooks write -sync %s\n' "${file}"
-        printf 'set-option buffer spell_tmp_file %s\n' "${file}"
+        path=$(mktemp -d "${TMPDIR:-/tmp}"/kak-spell.XXXXXXXX)
+        printf 'set-option buffer spell_tmp_dir "%s"\n' "${path}"
     }
+
+    execute-keys "<a-|>tee ""%opt{spell_tmp_dir}/${kak_main_reg_hash}""<ret>"
+
     evaluate-commands %sh{
         if [ $# -ge 1 ]; then
             if [ ${#1} -ne 2 ] && [ ${#1} -ne 5 ]; then
                 echo "echo -markup '{Error}Invalid language code (examples of expected format: en, en_US, en-US)'"
-                rm -rf "$(dirname "$kak_opt_spell_tmp_file")"
+                rm -rf "${kak_opt_spell_tmp_dir}"
                 exit 1
             else
                 options="-l '$1'"
@@ -28,35 +29,49 @@ Formats of language supported:
         fi

         {
-            sed 's/^/^/' "$kak_opt_spell_tmp_file" | eval "aspell --byte-offsets -a $options" 2>&1 | {
-                line_num=1
-                regions=$kak_timestamp
-                read line # drop the identification message
-                while read -r line; do
-                    case "$line" in
-                        [\#\&]*)
-                            if expr "$line" : '^&' >/dev/null; then
-                               pos=$(printf %s\\n "$line" | cut -d ' ' -f 4 | sed 's/:$//')
-                            else
-                               pos=$(printf %s\\n "$line" | cut -d ' ' -f 3)
-                            fi
-                            word=$(printf %s\\n "$line" | cut -d ' ' -f 2)
-                            len=$(printf %s "$word" | wc -c)
-                            regions="$regions $line_num.$pos+${len}|Error"
-                            ;;
-                        '') line_num=$((line_num + 1));;
-                        \*) ;;
-                        *) printf 'echo -markup %%{{Error}%s}\n' "${line}" | kak -p "${kak_session}";;
-                    esac
-                done
-                printf 'set-option "buffer=%s" spell_regions %s' "${kak_bufname}" "${regions}" \
-                    | kak -p "${kak_session}"
-            }
-            rm -rf $(dirname "$kak_opt_spell_tmp_file")
-        } </dev/null >/dev/null 2>&1 &
+            find "${kak_opt_spell_tmp_dir}" -type f | while read -r path_selection; do
+                sed 's/^/^/' "${path_selection}" | eval "aspell --byte-offsets -a $options" 2>&1 | {
+                    line_num=1
+                    regions=""
+                    read line # drop the identification message
+                    while read -r line; do
+                        case "$line" in
+                            [\#\&]*)
+                                if printf %s "${line}" | grep -q '^&' >/dev/null; then
+                                   pos=$(printf %s\\n "$line" | cut -d ' ' -f 4 | sed 's/:$//')
+                                else
+                                   pos=$(printf %s\\n "$line" | cut -d ' ' -f 3)
+                                fi
+                                word=$(printf %s\\n "$line" | cut -d ' ' -f 2)
+                                len=$(printf %s "$word" | wc -c)
+                                regions="$regions $line_num.$pos+${len}|Error"
+                                ;;
+                            '') line_num=$((line_num + 1));;
+                            \*) ;;
+                            *) printf 'echo -markup %%{{Error}%s}\n' "${line}" | kak -p "${kak_session}";;
+                        esac
+                    done
+                    printf 'eval -client "%s" set-option -add window spell_regions %s\n' "${kak_client}" "${regions}" \
+                        | kak -p "${kak_session}"
+                }
+            done
+
+            rm -rf "${kak_opt_spell_tmp_dir}"
+        } >/dev/null 2>&1 &
     }
 }

+define-command -params ..1 \
+    -docstring %{spell [<language>]: spell check the current buffer
+The first optional argument is the language against which the check will be performed
+Formats of language supported:
+ - ISO language code, e.g. 'en'
+ - language code above followed by a dash or underscore with an ISO country code, e.g. 'en-US'} \
+    spell %{ evaluate-commands -draft %{
+    execute-keys \%
+    spell-selection %arg{@}
+} }
+
 define-command spell-clear %{
     unset-option buffer spell_regions
 }
tylercecil commented 4 years ago

Perhaps before dealing with :spell-selection (which could be a good idea), Aspell does have filters and modes, which can deal with cases like "only spellcheck comments".

$ aspell dump modes
url            mode to skip URL like constructs (default mode)
html           mode for checking HTML documents
markdown       mode for checking Markdown/CommonMark documents
texinfo        mode for checking Texinfo documents
none           mode to disable all filters
sgml           mode for checking generic SGML/XML documents
comment        mode to check any lines starting with a #
tex            mode for checking TeX/LaTeX documents
email          mode for skipping quoted text in email messages
perl           mode for checking Perl comments and string literals
ccpp           mode for checking C++ comments and string literals
nroff          mode for checking Nroff documents

If we could expose an option to pass a mode (or even a filter?) That would make :spell a lot more useful, I think. Plus it would be leveraging the system more, rather than putting more into a kak file.

mreppen commented 4 years ago

:spell-selection would be very useful, and so would @tylercecil's suggestion for manual options, for which it is trivial to add basic support. I gave it a go by adding an option for passing any list of strings to the aspell call. Works wonders for TeX by passing --mode=tex and extra command filters.