@@ -622,4 +622,107 @@ M.treesitter = function(opts)
622622 core .fzf_exec (contents , opts )
623623end
624624
625+ M .spellcheck = function (opts )
626+ opts = config .normalize_opts (opts , " spellcheck" )
627+ if not opts then return end
628+
629+ if # vim .bo .spelllang == 0 then
630+ utils .info (" Spell language not set, use ':setl spl=...' to enable spell checking." )
631+ return
632+ end
633+
634+ -- Default to current buffer
635+ opts ._bufnr = tonumber (opts .bufnr ) or vim .api .nvim_get_current_buf ()
636+ opts ._bufname = path .basename (vim .api .nvim_buf_get_name (opts ._bufnr ))
637+ if not opts ._bufname or # opts ._bufname == 0 then
638+ opts ._bufname = utils .nvim_buf_get_name (opts ._bufnr )
639+ end
640+
641+ if utils .mode_is_visual () then
642+ local _ , sel = utils .get_visual_selection ()
643+ opts .start_line = opts .start_line or sel .start .line
644+ opts .end_line = opts .end_line or sel [" end" ].line
645+ end
646+
647+ local contents = function (cb )
648+ coroutine.wrap (function ()
649+ local co = coroutine.running ()
650+ local data = {}
651+
652+ -- Use vim.schedule to avoid
653+ -- E5560: vimL function must not be called in a lua loop callback
654+ vim .schedule (function ()
655+ local bufnr = opts ._bufnr
656+ local filepath = vim .api .nvim_buf_get_name (bufnr )
657+ if vim .api .nvim_buf_is_loaded (bufnr ) then
658+ data = vim .api .nvim_buf_get_lines (bufnr , 0 , - 1 , false )
659+ elseif vim .fn .filereadable (filepath ) ~= 0 then
660+ data = vim .fn .readfile (filepath , " " )
661+ end
662+ coroutine.resume (co )
663+ end )
664+
665+ -- wait for vim.schedule
666+ coroutine.yield ()
667+
668+ local offset = 0
669+ local start_line = opts .start_line or 1
670+ local end_line = opts .end_line or # data
671+ local lines = end_line - start_line + 1
672+
673+ if opts .start == " cursor" then
674+ -- start display from current line and wrap from bottom
675+ offset = core .CTX ().cursor [1 ] - start_line
676+ end
677+
678+ for i = 1 , lines do
679+ local lnum = i + offset
680+ if lnum > lines then
681+ lnum = lnum % lines
682+ end
683+ lnum = lnum + start_line - 1
684+
685+ local line , from , to = data [lnum ], 1 , nil
686+ repeat
687+ local word_separator = opts .word_separator or " [%s%p]"
688+ local function trim (s )
689+ return s :gsub (" ^" .. word_separator .. " +" , " " ):gsub (word_separator .. " +$" , " " )
690+ end
691+ from , to = string.find (line , " %w+" , from )
692+ local word = from and string.sub (line , from , to )
693+ local prefix = from and string.sub (line , from - 1 , from - 1 ) or " "
694+ local postfix = to and string.sub (line , to + 1 , to + 1 ) or " "
695+ local valid_word = word
696+ and (# prefix == 0 or prefix :match (" ^" .. word_separator ))
697+ and (# postfix == 0 or postfix :match (word_separator .. " $" ))
698+ if valid_word then
699+ local _ , lead = word :find (" ^" .. word_separator .. " +" )
700+ local spell = vim .spell .check (trim (word ))[1 ]
701+ if spell then
702+ cb (string.format (" [%s]%s%s:%s:%s\t\t %s" ,
703+ utils .ansi_codes [opts .hls .buf_nr ](tostring (opts ._bufnr )),
704+ utils .nbsp ,
705+ utils .ansi_codes [opts .hls .buf_name ](opts ._bufname ),
706+ utils .ansi_codes [opts .hls .buf_linenr ](tostring (lnum )),
707+ utils .ansi_codes [opts .hls .path_colnr ](tostring (from + (lead or 0 ))),
708+ trim (word )
709+ ), function (err )
710+ coroutine.resume (co )
711+ if err then cb (nil ) end
712+ end )
713+ coroutine.yield ()
714+ end
715+ end
716+ if from then from = to + 1 end
717+ until not from
718+ end
719+ cb (nil )
720+ end )()
721+ end
722+
723+ opts = core .set_header (opts , opts .headers or { " actions" })
724+
725+ core .fzf_exec (contents , opts )
726+ end
727+
625728return M
0 commit comments