diff options
author | Junio C Hamano <gitster@pobox.com> | 2008-02-13 11:04:58 -0800 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2008-02-13 11:04:58 -0800 |
commit | 41e2edf41a6d501f1b8beca7f1f0bcbe9296dcc2 (patch) | |
tree | 5a3e97b7c147242f3550a7318ec45480ecaa7480 /git-gui/lib | |
parent | Merge git://git.kernel.org/pub/scm/gitk/gitk (diff) | |
parent | git-gui: Automatically spell check commit messages as the user types (diff) | |
download | tgif-41e2edf41a6d501f1b8beca7f1f0bcbe9296dcc2.tar.xz |
Merge git://repo.or.cz/git-gui
* git://repo.or.cz/git-gui:
git-gui: Automatically spell check commit messages as the user types
git-gui: support Git Gui.app under OS X 10.5
git-gui: Update German translation.
git-gui: (i18n) Fix a bunch of still untranslated strings.
Diffstat (limited to 'git-gui/lib')
-rw-r--r-- | git-gui/lib/about.tcl | 5 | ||||
-rw-r--r-- | git-gui/lib/checkout_op.tcl | 6 | ||||
-rw-r--r-- | git-gui/lib/commit.tcl | 16 | ||||
-rw-r--r-- | git-gui/lib/index.tcl | 2 | ||||
-rw-r--r-- | git-gui/lib/merge.tcl | 5 | ||||
-rw-r--r-- | git-gui/lib/option.tcl | 41 | ||||
-rw-r--r-- | git-gui/lib/spellcheck.tcl | 363 |
7 files changed, 423 insertions, 15 deletions
diff --git a/git-gui/lib/about.tcl b/git-gui/lib/about.tcl index 719fc547b3..47be8eb97a 100644 --- a/git-gui/lib/about.tcl +++ b/git-gui/lib/about.tcl @@ -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 @@ -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" diff --git a/git-gui/lib/checkout_op.tcl b/git-gui/lib/checkout_op.tcl index f243966924..6e1411711b 100644 --- a/git-gui/lib/checkout_op.tcl +++ b/git-gui/lib/checkout_op.tcl @@ -280,7 +280,7 @@ The rescan will be automatically started now. } elseif {[is_config_true gui.trustmtime]} { _readtree $this } else { - ui_status {Refreshing file status...} + ui_status [mc "Refreshing file status..."] set fd [git_read update-index \ -q \ --unmerged \ @@ -320,7 +320,7 @@ method _readtree {} { set readtree_d {} $::main_status start \ [mc "Updating working directory to '%s'..." [_name $this]] \ - {files checked out} + [mc "files checked out"] set fd [git_read --stderr read-tree \ -m \ @@ -447,7 +447,7 @@ If you wanted to be on a branch, create one now starting from 'This Detached Che } else { repository_state commit_type HEAD MERGE_HEAD set PARENT $HEAD - ui_status "Checked out '$name'." + ui_status [mc "Checked out '%s'." $name] } delete_this } diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 947b201c32..40a7103557 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -218,7 +218,7 @@ A good commit message has the following format: return } - ui_status {Calling pre-commit hook...} + ui_status [mc "Calling pre-commit hook..."] set pch_error {} fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} fileevent $fd_ph readable \ @@ -233,7 +233,7 @@ proc commit_prehook_wait {fd_ph curHEAD msg_p} { if {[eof $fd_ph]} { if {[catch {close $fd_ph}]} { catch {file delete $msg_p} - ui_status {Commit declined by pre-commit hook.} + ui_status [mc "Commit declined by pre-commit hook."] hook_failed_popup pre-commit $pch_error unlock_index } else { @@ -256,7 +256,7 @@ proc commit_commitmsg {curHEAD msg_p} { return } - ui_status {Calling commit-msg hook...} + ui_status [mc "Calling commit-msg hook..."] set pch_error {} fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} fileevent $fd_ph readable \ @@ -271,7 +271,7 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} { if {[eof $fd_ph]} { if {[catch {close $fd_ph}]} { catch {file delete $msg_p} - ui_status {Commit declined by commit-msg hook.} + ui_status [mc "Commit declined by commit-msg hook."] hook_failed_popup commit-msg $pch_error unlock_index } else { @@ -284,7 +284,7 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} { } proc commit_writetree {curHEAD msg_p} { - ui_status {Committing changes...} + ui_status [mc "Committing changes..."] set fd_wt [git_read write-tree] fileevent $fd_wt readable \ [list commit_committree $fd_wt $curHEAD $msg_p] @@ -301,7 +301,7 @@ proc commit_committree {fd_wt curHEAD msg_p} { if {[catch {close $fd_wt} err]} { catch {file delete $msg_p} error_popup [strcat [mc "write-tree failed:"] "\n\n$err"] - ui_status {Commit failed.} + ui_status [mc "Commit failed."] unlock_index return } @@ -345,7 +345,7 @@ A rescan will be automatically started now. if {[catch {set cmt_id [eval git $cmd]} err]} { catch {file delete $msg_p} error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"] - ui_status {Commit failed.} + ui_status [mc "Commit failed."] unlock_index return } @@ -365,7 +365,7 @@ A rescan will be automatically started now. } err]} { catch {file delete $msg_p} error_popup [strcat [mc "update-ref failed:"] "\n\n$err"] - ui_status {Commit failed.} + ui_status [mc "Commit failed."] unlock_index return } diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index 30a244cc17..3c1fce7475 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -310,7 +310,7 @@ proc add_helper {txt paths} { update_index \ $txt \ $pathList \ - [concat $after {ui_status {Ready to commit.}}] + [concat $after {ui_status [mc "Ready to commit."]}] } } diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl index 63e14279c1..cc26b07808 100644 --- a/git-gui/lib/merge.tcl +++ b/git-gui/lib/merge.tcl @@ -116,8 +116,7 @@ method _start {} { lappend cmd HEAD lappend cmd $name - set msg [mc "Merging %s and %s" $current_branch $stitle] - ui_status "$msg..." + ui_status [mc "Merging %s and %s..." $current_branch $stitle] set cons [console::new [mc "Merge"] "merge $stitle"] console::exec $cons $cmd [cb _finish $cons] @@ -236,7 +235,7 @@ Continue with resetting the current changes?"] set fd [git_read --stderr read-tree --reset -u -v HEAD] fconfigure $fd -blocking 0 -translation binary fileevent $fd readable [namespace code [list _reset_wait $fd]] - $::main_status start [mc "Aborting"] {files reset} + $::main_status start [mc "Aborting"] [mc "files reset"] } else { unlock_index } diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index f812e5e89a..3bfa2edf1a 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl @@ -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] @@ -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 @@ -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] diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl new file mode 100644 index 0000000000..01c2c4f0d1 --- /dev/null +++ b/git-gui/lib/spellcheck.tcl @@ -0,0 +1,363 @@ +# git-gui spellchecking support through aspell +# Copyright (C) 2008 Shawn Pearce + +class spellcheck { + +field s_fd {} ; # pipe to aspell +field s_version ; # aspell version string +field s_lang ; # current language code + +field w_text ; # text widget we are spelling +field w_menu ; # context menu for the widget +field s_menuidx 0 ; # last index of insertion into $w_menu + +field s_i ; # timer registration for _run callbacks +field s_clear 0 ; # did we erase mispelled tags yet? +field s_seen [list] ; # lines last seen from $w_text in _run +field s_checked [list] ; # lines already checked +field s_pending [list] ; # [$line $data] sent to aspell +field s_suggest ; # array, list of suggestions, keyed by misspelling + +constructor init {pipe_fd ui_text ui_menu} { + set w_text $ui_text + set w_menu $ui_menu + + _connect $this $pipe_fd + return $this +} + +method _connect {pipe_fd} { + fconfigure $pipe_fd \ + -encoding utf-8 \ + -eofchar {} \ + -translation lf + + if {[gets $pipe_fd s_version] <= 0} { + close $pipe_fd + error [mc "Not connected to aspell"] + } + if {{@(#) } ne [string range $s_version 0 4]} { + close $pipe_fd + error [strcat [mc "Unrecognized aspell version"] ": $s_version"] + } + set s_version [string range $s_version 5 end] + + puts $pipe_fd ! ; # enable terse mode + puts $pipe_fd {$$cr master} ; # fetch the language + flush $pipe_fd + + gets $pipe_fd s_lang + regexp {[/\\]([^/\\]+)\.[^\.]+$} $s_lang _ s_lang + + if {$::default_config(gui.spellingdictionary) eq {} + && [get_config gui.spellingdictionary] eq {}} { + set ::default_config(gui.spellingdictionary) $s_lang + } + + if {$s_fd ne {}} { + catch {close $s_fd} + } + set s_fd $pipe_fd + + fconfigure $s_fd -blocking 0 + fileevent $s_fd readable [cb _read] + + $w_text tag conf misspelled \ + -foreground red \ + -underline 1 + bind_button3 $w_text [cb _popup_suggest %X %Y @%x,%y] + + array unset s_suggest + set s_seen [list] + set s_checked [list] + set s_pending [list] + _run $this +} + +method lang {{n {}}} { + if {$n ne {} && $s_lang ne $n} { + set spell_cmd [list |] + lappend spell_cmd aspell + lappend spell_cmd --master=$n + lappend spell_cmd --mode=none + lappend spell_cmd --encoding=UTF-8 + lappend spell_cmd pipe + _connect $this [open $spell_cmd r+] + } + return $s_lang +} + +method version {} { + return "$s_version, $s_lang" +} + +method stop {} { + while {$s_menuidx > 0} { + $w_menu delete 0 + incr s_menuidx -1 + } + $w_text tag delete misspelled + + catch {close $s_fd} + catch {after cancel $s_i} + set s_fd {} + set s_i {} + set s_lang {} +} + +method _popup_suggest {X Y pos} { + while {$s_menuidx > 0} { + $w_menu delete 0 + incr s_menuidx -1 + } + + set b_loc [$w_text index "$pos wordstart"] + set e_loc [_wordend $this $b_loc] + set orig [$w_text get $b_loc $e_loc] + set tags [$w_text tag names $b_loc] + + if {[lsearch -exact $tags misspelled] >= 0} { + if {[info exists s_suggest($orig)]} { + set cnt 0 + foreach s $s_suggest($orig) { + if {$cnt < 5} { + $w_menu insert $s_menuidx command \ + -label $s \ + -command [cb _replace $b_loc $e_loc $s] + incr s_menuidx + incr cnt + } else { + break + } + } + } else { + $w_menu insert $s_menuidx command \ + -label [mc "No Suggestions"] \ + -state disabled + incr s_menuidx + } + $w_menu insert $s_menuidx separator + incr s_menuidx + } + + $w_text mark set saved-insert insert + tk_popup $w_menu $X $Y +} + +method _replace {b_loc e_loc word} { + $w_text configure -autoseparators 0 + $w_text edit separator + + $w_text delete $b_loc $e_loc + $w_text insert $b_loc $word + + $w_text edit separator + $w_text configure -autoseparators 1 + $w_text mark set insert saved-insert +} + +method _restart_timer {} { + set s_i [after 300 [cb _run]] +} + +proc _match_length {max_line arr_name} { + upvar $arr_name a + + if {[llength $a] > $max_line} { + set a [lrange $a 0 $max_line] + } + while {[llength $a] <= $max_line} { + lappend a {} + } +} + +method _wordend {pos} { + set pos [$w_text index "$pos wordend"] + set tags [$w_text tag names $pos] + while {[lsearch -exact $tags misspelled] >= 0} { + set pos [$w_text index "$pos +1c"] + set tags [$w_text tag names $pos] + } + return $pos +} + +method _run {} { + set cur_pos [$w_text index {insert -1c}] + set cur_line [lindex [split $cur_pos .] 0] + set max_line [lindex [split [$w_text index end] .] 0] + _match_length $max_line s_seen + _match_length $max_line s_checked + + # Nothing in the message buffer? Nothing to spellcheck. + # + if {$cur_line == 1 + && $max_line == 2 + && [$w_text get 1.0 end] eq "\n"} { + array unset s_suggest + _restart_timer $this + return + } + + set active 0 + for {set n 1} {$n <= $max_line} {incr n} { + set s [$w_text get "$n.0" "$n.end"] + + # Don't spellcheck the current line unless we are at + # a word boundary. The user might be typing on it. + # + if {$n == $cur_line + && ![regexp {^\W$} [$w_text get $cur_pos insert]]} { + + # If the current word is mispelled remove the tag + # but force a spellcheck later. + # + set tags [$w_text tag names $cur_pos] + if {[lsearch -exact $tags misspelled] >= 0} { + $w_text tag remove misspelled \ + "$cur_pos wordstart" \ + [_wordend $this $cur_pos] + lset s_seen $n $s + lset s_checked $n {} + } + + continue + } + + if {[lindex $s_seen $n] eq $s + && [lindex $s_checked $n] ne $s} { + # Don't send empty lines to Aspell it doesn't check them. + # + if {$s eq {}} { + lset s_checked $n $s + continue + } + + # Don't send typical s-b-o lines as the emails are + # almost always misspelled according to Aspell. + # + if {[regexp -nocase {^[a-z-]+-by:.*<.*@.*>$} $s]} { + $w_text tag remove misspelled "$n.0" "$n.end" + lset s_checked $n $s + continue + } + + puts $s_fd ^$s + lappend s_pending [list $n $s] + set active 1 + } else { + # Delay until another idle loop to make sure we don't + # spellcheck lines the user is actively changing. + # + lset s_seen $n $s + } + } + + if {$active} { + set s_clear 1 + flush $s_fd + } else { + _restart_timer $this + } +} + +method _read {} { + while {[gets $s_fd line] >= 0} { + set lineno [lindex $s_pending 0 0] + + if {$s_clear} { + $w_text tag remove misspelled "$lineno.0" "$lineno.end" + set s_clear 0 + } + + if {$line eq {}} { + lset s_checked $lineno [lindex $s_pending 0 1] + set s_pending [lrange $s_pending 1 end] + set s_clear 1 + continue + } + + set sugg [list] + switch -- [string range $line 0 1] { + {& } { + set line [split [string range $line 2 end] :] + set info [split [lindex $line 0] { }] + set orig [lindex $info 0] + set offs [lindex $info 2] + foreach s [split [lindex $line 1] ,] { + lappend sugg [string range $s 1 end] + } + } + {# } { + set info [split [string range $line 2 end] { }] + set orig [lindex $info 0] + set offs [lindex $info 1] + } + default { + puts stderr "<spell> $line" + continue + } + } + + incr offs -1 + set b_loc "$lineno.$offs" + set e_loc [$w_text index "$lineno.$offs wordend"] + set curr [$w_text get $b_loc $e_loc] + + # At least for English curr = "bob", orig = "bob's" + # so Tk didn't include the 's but Aspell did. We + # try to round out the word. + # + while {$curr ne $orig + && [string equal -length [llength $curr] $curr $orig]} { + set n_loc [$w_text index "$e_loc +1c"] + set n_curr [$w_text get $b_loc $n_loc] + if {$n_curr eq $curr} { + break + } + set curr $n_curr + set e_loc $n_loc + } + + if {$curr eq $orig} { + $w_text tag add misspelled $b_loc $e_loc + if {[llength $sugg] > 0} { + set s_suggest($orig) $sugg + } else { + unset -nocomplain s_suggest($orig) + } + } else { + unset -nocomplain s_suggest($orig) + } + } + + fconfigure $s_fd -block 1 + if {[eof $s_fd]} { + if {![catch {close $s_fd} err]} { + set err [mc "unexpected eof from aspell"] + } + catch {after cancel $s_i} + $w_text tag remove misspelled 1.0 end + error_popup [strcat "Spell Checker Failed" "\n\n" $err] + return + } + fconfigure $s_fd -block 0 + + if {[llength $s_pending] == 0} { + _restart_timer $this + } +} + +proc available_langs {} { + set langs [list] + catch { + set fd [open [list | aspell dump dicts] r] + while {[gets $fd line] >= 0} { + if {$line eq {}} continue + lappend langs $line + } + close $fd + } + return $langs +} + +} |