tk-latex-editor/lib/modules/spellcheck.tcl

217 lines
8.0 KiB
Tcl
Raw Normal View History

2017-07-13 12:45:37 +03:00
#########################################################
# Tk LaTeX Editor
# Distributed under GNU Public License
# Author: Sergey Kalinin (BanZaj) banzaj@lrn.ru
# Copyright (c) "CONERO lab", 2002, http://conero.lrn.ru
#########################################################
# Spell Checking procedure
# required Ispell, Msgcat
# Author: Victor Wagner (Vitus) http://www.ice.ru/~vitus
#
# Usage: "SpellCheck text_widget"
#########################################################
proc SpellCheck {text} {
global ispell_lib ispell_language ispell_startindex nb color font
set node [$nb raise]
if {[winfo exists .spell]} {
return
}
if {$node == "newproj" || $node == "settings" || $node == "about" || $node == ""} {
return
}
# Build dictionary list
foreach dict [glob [file join $ispell_lib *.hash]] {
file stat $dict foo
set lang [file rootname [file tail $dict]]
if {$lang == "default"} {
set default_inode $foo(ino)
} else {
lappend langs $lang
set lang_nodes($foo(ino)) $lang;
}
}
if {![info exists ispell_language]} {
if {[info exists default_inode ]&&[info exists lang_nodes($default_inode)]} {
set ispell_language $lang_nodes($default_inode)
} else {
set ispell_language [lindex $langs 0]
}
}
toplevel .spell -class Notepad
wm title .spell [::msgcat::mc "Spell check"]
wm transient .spell .
wm resizable .spell 0 0
frame .spell.lang -relief raised -borderwidth 1 -background $color(bg)
label .spell.lang.l -text [::msgcat::mc "Language"] -background $color(bg) -font $font(normal)
eval tk_optionMenu .spell.lang.m ispell_language $langs -background $color(bg) -font $font(normal)
pack .spell.lang.l .spell.lang.m -side left -padx 5
pack .spell.lang.m -side left -padx 5 -fill x -expand 1
frame .spell.start -relief raised -borderwidth 1 -background $color(bg)
radiobutton .spell.start.all -text [::msgcat::mc "Entire file"] -background $color(bg)\
-variable ispell_startindex -value "1.0" -anchor w -font $font(normal)
radiobutton .spell.start.ins -text [::msgcat::mc "From cursor"] -background $color(bg)\
-variable ispell_startindex -value insert -anchor w -font $font(normal)
pack .spell.start.all .spell.start.ins -side top -pady 1 -expand y -fill x
frame .spell.b -relief raised -borderwidth 1 -background $color(bg)
button .spell.b.ok -text [::msgcat::mc "Start"] -relief groove -background $color(bg)\
-command "DoSpellCheck $text" -font $font(normal)
button .spell.b.cancel -text [::msgcat::mc "Cancel"] -relief groove -background $color(bg)\
-command "destroy .spell" -font $font(normal)
pack .spell.b.ok .spell.b.cancel -side left -padx 10
pack .spell.lang -side top -fill x
pack .spell.start -side top -fill both -expand 1
pack .spell.b -side top -fill x
bind .spell <Escape> "destroy .spell"
}
proc DoSpellCheck {text} {
global ispell_file ispell_startindex ispell_language spellAddWords color font
eval destroy [winfo children .spell]
wm transient .spell .
entry .spell.word -exportselection false
listbox .spell.misses -yscrollcommand ".spell.y set" -exportselection false -background $color(bg)
scrollbar .spell.y -command ".spell.misses yview" -background $color(bg)
button .spell.ok -text [::msgcat::mc "Accept"] -command "SpellOk $text" -background $color(bg) \
-font $font(normal)
button .spell.accept -text [::msgcat::mc "Next"] -command "SpellAccept @" -background $color(bg) \
-font $font(normal)
button .spell.adddict -text [::msgcat::mc "Add to dict"] -command "SpellAccept *" -background $color(bg) \
-font $font(normal)
button .spell.adddict2 -text [::msgcat::mc "Add lowercase"] -command "SpellAccept &" -background $color(bg) \
-font $font(normal)
button .spell.stop -text [::msgcat::mc "Stop checking"] -command "SpellStop $text" -background $color(bg) \
-font $font(normal)
grid .spell.word - - -sticky news -padx 5 -pady 5
grid .spell.misses .spell.y .spell.ok -sticky news
grid x x .spell.accept -sticky news
grid x x .spell.adddict -sticky news
grid x x .spell.adddict2 -sticky news
grid x x .spell.stop -sticky news
grid configure .spell.ok .spell.accept .spell.adddict .spell.adddict2 .spell.stop -padx 5 -pady 3
grid configure .spell.misses .spell.y -rowspan 5
focus .spell.word
wm protocol .spell WM_DELETE_WINDOW "SpellStop $text"
set ispell_file [open "|ispell -a -d $ispell_language" w+]
fconfigure $ispell_file -buffering none -blocking yes
gets $ispell_file
puts $ispell_file "!"
set spellAddWords {}
$text mark set insert $ispell_startindex
while {[SpellNextLine $text]} {
while {[gets $ispell_file responce]>0} {
switch -exact -- [string range $responce 0 0] {
"*" -
"+" -
"-" {continue}
"&" -
"?" {regexp {^. +([^ ]+) +[0-9]+ +([0-9]+):(.*)$} $responce foo \
orig offset guesses
SpellInteract $orig $offset $guesses $text
}
"#" {regexp {^. +([^ ]+) +([0-9]+)} $responce foo orig offset
SpellInteract $orig $offset {} $text
}
}
}
$text mark set insert "spellstart linestart + 1 lines"
}
close $ispell_file
eval destroy [winfo children .spell]
label .spell.l -text [::msgcat::mc "Checking complete"] -background $color(bg)
button .spell.ok -text [::msgcat::mc "Ok"] -command {destroy .spell} -background $color(bg)
wm protocol .spell WM_DELETE_WINDOW {destroy .spell}
pack .spell.l .spell.ok -side top -padx 5 -pady 10
}
proc SpellNextLine {text} {
global spellOffset ispell_file spellAddWords
set spellOffset -1
$text see insert
$text mark set spellstart insert
set line [$text get insert "insert lineend"]
if [$text compare insert >= "end-1char"] {
return 0
}
foreach word $spellAddWords {
puts "Adding to dictionary $word"
puts $ispell_file $word
}
set spellAddWords {}
puts $ispell_file "^$line"
return 1
}
proc SpellInteract {word offset guesses text} {
.spell.word delete 0 end
.spell.word insert 0 $word
$text tag remove sel 0.0 end
global spellOffset
incr offset $spellOffset
$text tag add sel "spellstart+$offset chars" "spellstart+[expr $offset+\
[string length $word]] chars"
global spellHighlight
set spellHighlight [$text tag ranges sel]
$text see sel.first
$text mark set insert sel.first
.spell.misses delete 0 end
if [string length $guesses] {
foreach miss [split $guesses ","] {
.spell.misses insert end [string trim $miss]
}
}
global spellInteractResult
vwait spellInteractResult
return -code $spellInteractResult
}
proc SpellOk {text} {
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> .spell.word <20><><EFBFBD> .spell.misses
global spellInteractResult spellHighlight
if [llength [.spell.misses curselection]] {
set word [.spell.misses get [.spell.misses curselection]]
} else {
set word [.spell.word get]
}
if {![llength [$text tag ranges sel]]} {
global spellHighilight
eval $text tag add sel $spellHighlight
}
set oldword [$text get sel.first sel.last]
if {[string compare $oldword $word]} {
$text delete sel.first sel.last
$text insert insert $word sel
global changed spellOffset
set changed 1
incr spellOffset [expr [string length $word]-[string length $oldword]]
}
set spellInteractResult ok
}
proc SpellAccept {cmd} {
global spellAddWords spellInteractResult
lappend spellAddWords "$cmd[.spell.word get]"
set spellInteractResult ok
}
proc SpellStop {text} {
global ispell_file
$text mark unset spellstart
catch {close $ispell_file}
destroy .spell
set spellInteractResult return
}