Skip to content

Commit

Permalink
git-gui: Automatically spell check commit messages as the user types
Browse files Browse the repository at this point in the history
Many user friendly tools like word processors, email editors and web
browsers allow users to spell check the message they are writing
as they type it, making it easy to identify a common misspelling
of a word and correct it on the fly.

We now open a bi-directional pipe to Aspell and feed the message
text the user is editing off to the program about once every 300
milliseconds.  This is frequent enough that the user sees the results
almost immediately, but is not so frequent as to cause significant
additional load on the system.  If the user has modified the message
text during the last 300 milliseconds we delay until the next period,
ensuring that we avoid flooding the Aspell process with a lot of
text while the user is actively typing their message.

We wait to send the current message buffer to Aspell until the user
is at a word boundary, thus ensuring that we are not likely to ask
for misspelled word detection on a word that the user is actively
typing, as most words are misspelled when only partially typed,
even if the user has thus far typed it correctly.

Misspelled words are highlighted in red and are given an underline,
causing the word to stand out from the others in the buffer.  This is
a very common user interface idiom for displaying misspelled words,
but differs from one platform to the next in slight variations.
For example the Mac OS X system prefers using a dashed red underline,
leaving the word in the original text color.  Unfortunately the
control that Tk gives us over text display is not powerful enough
to handle such formatting so we have to work with the least common
denominator.

The top suggestions for a misspelling are saved in an array and
offered to the user when they right-click (or on the Mac ctrl-click)
a misspelled word.  Selecting an entry from this menu will replace
the misspelling with the correction shown.  Replacement is integrated
with the undo/redo stack so undoing a replacement will restore the
misspelled original text.

If Aspell could not be started during git-gui launch we silently eat
the error and run without spell checking support.  This way users
who do not have Aspell in their $PATH can continue to use git-gui,
although they will not get the advanced spelling functionality.

If Aspell started successfully the version line and language are
shown in git-gui's about box, below the Tcl/Tk versions.  This way
the user can verify the Aspell function has been activated.

If Aspell crashes while we are running we inform the user with an
error dialog and then disable Aspell entirely for the rest of this
git-gui session.  This prevents us from fork-bombing the system
with Aspell instances that always crash when presented with the
current message text, should there be a bug in either Aspell or in
git-gui's output to it.

We escape all input lines with ^, as recommended by the Aspell manual
page, as this allows Aspell to properly ignore any input line that is
otherwise looking like a command (e.g. ! to enable terse output).  By
using this escape however we need to correct all word offsets by -1 as
Aspell is apparently considering the ^ escape to be part of the line's
character count, but our Tk text widget obviously does not.

Available dictionaries are offered in the Options dialog, allowing
the user to select the language they want to spellcheck commit
messages with for the current repository, as well as the global
user setting that all repositories inherit.

Special thanks to Adam Flott for suggesting connecting git-gui
to Aspell for the purpose of spell checking the commit message,
and to Wincent Colaiuta for the idea to wait for a word boundary
before passing the message over for checking.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
  • Loading branch information
Shawn O. Pearce committed Feb 12, 2008
1 parent 88965d1 commit 95b002e
Show file tree
Hide file tree
Showing 4 changed files with 442 additions and 1 deletion.
34 changes: 33 additions & 1 deletion git-gui.sh
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ set default_config(gui.pruneduringfetch) false
set default_config(gui.trustmtime) false
set default_config(gui.diffcontext) 5
set default_config(gui.newbranchtemplate) {}
set default_config(gui.spellingdictionary) {}
set default_config(gui.fontui) [font configure font_ui]
set default_config(gui.fontdiff) [font configure font_diff]
set font_descs {
Expand Down Expand Up @@ -1683,6 +1684,7 @@ set is_quitting 0
proc do_quit {} {
global ui_comm is_quitting repo_config commit_type
global GITGUI_BCK_exists GITGUI_BCK_i
global ui_comm_spell
if {$is_quitting} return
set is_quitting 1
Expand Down Expand Up @@ -1710,6 +1712,12 @@ proc do_quit {} {
}
}
# -- Cancel our spellchecker if its running.
#
if {[info exists ui_comm_spell]} {
$ui_comm_spell stop
}
# -- Remove our editor backup, its not needed.
#
after cancel $GITGUI_BCK_i
Expand Down Expand Up @@ -2454,7 +2462,7 @@ $ctxm add separator
$ctxm add command \
-label [mc "Sign Off"] \
-command do_signoff
bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
set ui_comm_ctxm $ctxm
# -- Diff Header
#
Expand Down Expand Up @@ -2857,6 +2865,30 @@ if {[winfo exists $ui_comm]} {
}
backup_commit_buffer
# -- If the user has aspell available we can drive it
# in pipe mode to spellcheck the commit message.
#
set spell_cmd [list |]
set spell_dict [get_config gui.spellingdictionary]
lappend spell_cmd aspell
if {$spell_dict ne {}} {
lappend spell_cmd --master=$spell_dict
}
lappend spell_cmd --mode=none
lappend spell_cmd --encoding=utf-8
lappend spell_cmd pipe
if {$spell_dict eq {none}
|| [catch {set spell_fd [open $spell_cmd r+]} spell_err]} {
bind_button3 $ui_comm [list tk_popup $ui_comm_ctxm %X %Y]
} else {
set ui_comm_spell [spellcheck::init \
$spell_fd \
$ui_comm \
$ui_comm_ctxm \
]
}
unset -nocomplain spell_cmd spell_fd spell_err spell_dict
}
lock_index begin-read
Expand Down
5 changes: 5 additions & 0 deletions lib/about.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
proc do_about {} {
global appvers copyright oguilib
global tcl_patchLevel tk_patchLevel
global ui_comm_spell

set w .about_dialog
toplevel $w
Expand Down Expand Up @@ -40,6 +41,10 @@ proc do_about {} {
append v "Tcl version $tcl_patchLevel"
append v ", Tk version $tk_patchLevel"
}
if {[info exists ui_comm_spell]} {
append v "\n"
append v [$ui_comm_spell version]
}

set d {}
append d "git wrapper: $::_git\n"
Expand Down
41 changes: 41 additions & 0 deletions lib/option.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ proc save_config {} {
global default_config font_descs
global repo_config global_config
global repo_config_new global_config_new
global ui_comm_spell

foreach option $font_descs {
set name [lindex $option 0]
Expand Down Expand Up @@ -52,11 +53,23 @@ proc save_config {} {
set repo_config($name) $value
}
}

if {[info exists repo_config(gui.spellingdictionary)]} {
set value $repo_config(gui.spellingdictionary)
if {$value eq {none}} {
if {[info exists ui_comm_spell]} {
$ui_comm_spell stop
}
} elseif {[info exists ui_comm_spell]} {
$ui_comm_spell lang $value
}
}
}

proc do_options {} {
global repo_config global_config font_descs
global repo_config_new global_config_new
global ui_comm_spell

array unset repo_config_new
array unset global_config_new
Expand Down Expand Up @@ -159,6 +172,34 @@ proc do_options {} {
}
}

set all_dicts [linsert \
[spellcheck::available_langs] \
0 \
none]
incr optid
foreach f {repo global} {
if {![info exists ${f}_config_new(gui.spellingdictionary)]} {
if {[info exists ui_comm_spell]} {
set value [$ui_comm_spell lang]
} else {
set value none
}
set ${f}_config_new(gui.spellingdictionary) $value
}

frame $w.$f.$optid
label $w.$f.$optid.l -text [mc "Spelling Dictionary:"]
eval tk_optionMenu $w.$f.$optid.v \
${f}_config_new(gui.spellingdictionary) \
$all_dicts
pack $w.$f.$optid.l -side left -anchor w -fill x
pack $w.$f.$optid.v -side left -anchor w \
-fill x -expand 1 \
-padx 5
pack $w.$f.$optid -side top -anchor w -fill x
}
unset all_dicts

set all_fonts [lsort [font families]]
foreach option $font_descs {
set name [lindex $option 0]
Expand Down
Loading

0 comments on commit 95b002e

Please sign in to comment.