1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
-- Copyright (c) 2017-2019 Florian Fischer. All rights reserved.
-- Use of this source code is governed by a MIT license found in the LICENSE file.
local spellcheck = {}
spellcheck.lang = os.getenv("LANG"):sub(0,5) or "en_US"
local supress_output = ">/dev/null 2>/dev/null"
if os.execute("type enchant "..supress_output) then
spellcheck.cmd = "enchant -d %s -a"
spellcheck.list_cmd = "enchant -l -d %s -a"
elseif os.execute("type enchant-2 "..supress_output) then
spellcheck.cmd = "enchant-2 -d %s -a"
spellcheck.list_cmd = "enchant-2 -l -d %s -a"
elseif os.execute("type aspell "..supress_output) then
spellcheck.cmd = "aspell -l %s -a"
spellcheck.list_cmd = "aspell list -l %s -a"
elseif os.execute("type hunspell "..supress_output) then
spellcheck.cmd = "hunspell -d %s"
spellcheck.list_cmd = "hunspell -l -d %s"
else
return nil
end
spellcheck.enabled = {}
local ignored = {}
local last_viewport, last_typos = nil, ""
vis.events.subscribe(vis.events.WIN_HIGHLIGHT, function(win)
if not spellcheck.enabled[win] or not win:style_define(42, "fore:red") then
return false
end
local viewport = win.viewport
local viewport_text = win.file:content(viewport)
local typos = ""
if last_viewport == viewport_text then
typos = last_typos
else
local cmd = spellcheck.list_cmd:format(spellcheck.lang)
local ret, so, se = vis:pipe(win.file, viewport, cmd)
if ret ~= 0 then
vis:message("calling " .. cmd .. " failed ("..se..")")
return false
end
typos = so or ""
end
local corrections_iter = typos:gmatch("(.-)\n")
local index = 0
for typo in corrections_iter do
if not ignored[typo] then
local start, finish = viewport_text:find(typo, index, true)
win:style(42, viewport.start + start - 1, viewport.start + finish)
index = finish
end
end
last_viewport = viewport_text
last_typos = typos
return true
end)
vis:map(vis.modes.NORMAL, "<C-w>e", function(keys)
spellcheck.enabled[vis.win] = true
return 0
end, "Enable spellchecking in the current window")
vis:map(vis.modes.NORMAL, "<C-w>d", function(keys)
spellcheck.enabled[vis.win] = nil
-- force new highlight
vis.win:draw()
return 0
end, "Disable spellchecking in the current window")
-- toggle spellchecking on <F7>
-- <F7> is used by some word processors (LibreOffice) for spellchecking
-- Thanks to @leorosa for the hint.
vis:map(vis.modes.NORMAL, "<F7>", function(keys)
if not spellcheck.enabled[vis.win] then
spellcheck.enabled[vis.win] = true
else
spellcheck.enabled[vis.win] = nil
vis.win:draw()
end
return 0
end, "Toggle spellchecking in the current window")
vis:map(vis.modes.NORMAL, "<C-w>w", function(keys)
local win = vis.win
local file = win.file
local pos = win.selection.pos
if not pos then return end
local range = file:text_object_word(pos);
if not range then return end
if range.start == range.finish then return end
local cmd = spellcheck.cmd:format(spellcheck.lang)
local ret, so, se = vis:pipe(win.file, range, cmd)
if ret ~= 0 then
vis:message("calling " .. cmd .. " failed ("..se..")")
return false
end
local suggestions = nil
local answer_line = so:match(".-\n(.-)\n.*")
local first_char = answer_line:sub(0,1)
if first_char == "*" then
vis:info(file:content(range).." is correctly spelled")
return true
elseif first_char == "#" then
vis:info("No corrections available for "..file:content(range))
return false
elseif first_char == "&" then
suggestions = answer_line:match("& %S+ %d+ %d+: (.*)")
else
vis:info("Unknown answer: "..answer_line)
return false
end
-- select a correction
local cmd = 'printf "' .. suggestions:gsub(", ", "\\n") .. '\\n" | vis-menu'
local f = io.popen(cmd)
local correction = f:read("*all")
f:close()
-- trim correction
correction = correction:match("^%s*(.-)%s*$")
if correction ~= "" then
win.file:delete(range)
win.file:insert(range.start, correction)
end
win.selection.pos = pos
win:draw()
return 0
end, "Correct misspelled word")
vis:map(vis.modes.NORMAL, "<C-w>i", function(keys)
local win = vis.win
local file = win.file
local pos = win.selection.pos
if not pos then return end
local range = file:text_object_word(pos);
if not range then return end
if range.start == range.finish then return end
ignored[file:content(range)] = true
win:draw()
return 0
end, "Ignore misspelled word")
vis:option_register("spelllang", "string", function(value, toggle)
spellcheck.lang = value
vis:info("Spellchecking language is now "..value)
-- force new highlight
last_viewport = nil
return true
end, "The language used for spellchecking")
return spellcheck
|