Skip to content

Instantly share code, notes, and snippets.

@rhaberkorn
Created October 19, 2024 12:22
Show Gist options
  • Save rhaberkorn/62996b0c3a9d618f389f5bdb81390e48 to your computer and use it in GitHub Desktop.
Save rhaberkorn/62996b0c3a9d618f389f5bdb81390e48 to your computer and use it in GitHub Desktop.
Primitive syntax-aware spell checker for the SciTE editor
-- This is a spell checker 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment