I was recently doing some writing with a word-count limit, and I wanted a quick way to count words in Kakoune. It’s easy enough to map a key to %|wc -w<ret> but that replaces the buffer with the word-count, which is quite disruptive. Instead, I came up with this approach:
define-command word-count \
-params 0 \
-docstring "Count words in the current buffer" \
%{
evaluate-commands -save-regs "^c" %{
# Save the current selections
execute-keys -save-regs "" Z
# Select non-whitespace spans and count them
execute-keys <percent>S\s+<ret>
set-register c "%reg{#}"
# Restore the original selections
execute-keys -save-regs "" z
# Report our result
echo Word count: "%reg{c}"
}
}
map -docstring "Word count" global user w ': word-count<ret>'
It’s a bit more complex than I expected - my first attempt worked inside a draft context so the selection-changes would automatically be undone, but that also meant the echo would be undone as well. If I save and restore the selections manually, I can run the echo outside a draft context and see the result.
Unfortunately, if I echo the result then restore the selection, the result is immediately covered by the “selections restored” message. Thus, I have to store the selection in another register, restore the selections, then echo the result so I can see it.
One way that you can simplify this command is by letting -draft do the Z-z dance.
And let clippy report the result of course
define-command word-count \
-params 0 \
-docstring "Count words in the current buffer" \
%{
evaluate-commands -save-regs "c" %{
evaluate-commands -draft %{
# Select non-whitespace spans and count them
execute-keys '%S\s+<ret>'
set-register c "%reg{#}"
}
# Report our result
info "Word count: %reg{c}"
}
}
Just grab the regex, it was a lazy copy and paste effort.
fun String.countLines(): Int {
return Regex("\r\n|\r|\n")
.findAll(this)
.count() + 1
}
fun String.countParagraphs(): Int {
return Regex("[^\r\n]+((\r|\n|\r\n)[^\r\n]+)*")
.findAll(this)
.count()
}
fun String.countSentences(): Int {
return Regex("(?<=[\'\"\"A-Za-z0-9][.!?])\\s+(?=[A-Z])")
.findAll(this)
.count()
}
fun String.countWords(): Int {
return Regex("\\b[^\\d\\W]+\\b")
.findAll(this)
.count()
}