-- Primitive syntax-aware spell checker for the SciTE editor based on aspell. -- It only requires the aspell CLI tool to be installed, but perhaps it won't work on -- Windows. -- You can just copy this into SciTEStartup.lua. -- -- TODO: Dictionaries should be configurable per file -- TODO: Support named styles in aspell.styles.lexer? -- -- The following should be copied into SciTEUser.properties and adapted: --[[ aspell.enabled=1 # Comma-separated list of dictionaries aspell.dicts=en_US # Restrict spell checking to certain lexer styles aspell.styles.lua=1,2,3 command.name.9.*=Manuall Spell Checking command.mode.9.*=subsystem:lua,savebefore:no command.9.*=aspell command.name.10.*=Toggle Automatic Spell Checking command.mode.10.*=subsystem:lua,savebefore:no command.10.*=aspell_toggle ]] function aspell_toggle() props['aspell.enabled'] = props['aspell.enabled'] == '1' and '0' or '1' if props['aspell.enabled'] == "1" then aspell() else editor.IndicatorCurrent = 2 editor:IndicatorClearRange(0, editor.Length) end end function aspell() local styles = props['aspell.styles.'..editor.LexerLanguage] local styles_table = {} for style in styles:gmatch("%d+") do styles_table[tonumber(style)] = true end local in_name = os.tmpname() local in_file = io.open(in_name, 'w') in_file:write('^') -- NOTE: Unfortunately, SCI_GETSTYLEDTEXT (editor:GetStyledText()) is not exposed. for pos = 0, editor.Length-1 do local c = editor.CharAt[pos] & 0xFF if c == 10 then -- Each line is prefixed with ^ since ispell/aspell accepts special -- commands as well in_file:write('\n^') else local must_spellcheck = styles == "" or styles_table[editor.StyleAt[pos]] in_file:write(must_spellcheck and string.char(c) or ' ') end end in_file:close() editor.IndicatorCurrent = 2 editor.IndicStyle[2] = INDIC_SQUIGGLEPIXMAP editor:IndicatorClearRange(0, editor.Length) local out = io.popen('aspell -d '..props['aspell.dicts'].. ' --dont-guess --dont-suggest --byte-offsets -a <'..in_name) local line = 0 for cmd in out:lines() do if cmd == "" then -- no more commands on the current line line = line + 1 elseif cmd:sub(1, 1) == "#" then -- word not found local word, offset = cmd:match("^. ([^ ]+) (%d+)$") editor:IndicatorFillRange(editor:PositionFromLine(line)+offset-1, #word) end end out:close() os.remove(in_name) end function OnOpen() if props['aspell.enabled'] == '0' then return end -- Make sure that everything is already styled as we spell check per style: editor:Colourise(0, -1) aspell() end function OnChar(new_chr) -- Called on every inserted character if props['aspell.enabled'] == '0' then return end local wordchars = editor.WordChars -- NOTE: new_chr is a string but not Unicode-aware. -- FIXME: Even better would be spell checking only chunks of recently styled text. if not wordchars:find(new_chr, 1, true) then aspell() end end