1#!/usr/local/bin/perl -w 2 3# ** This script is a 10-minutes-hack, so it's EXPERIMENTAL. ** 4# 5# Requires: 6# - Irssi 0.8.12 or newer (http://irssi.org/). 7# - GNU Aspell with appropriate dictionaries (http://aspell.net/). 8# - Perl module Text::Aspell (available from CPAN). 9# 10# 11# Description: 12# Works as you type, printing suggestions when Aspell thinks 13# your last word was misspelled. 14# It also adds suggestions to the list of tabcompletions, 15# so once you know last word is wrong, you can go back 16# and tabcomplete through what Aspell suggests. 17# 18# 19# Settings: 20# 21# spellcheck_languages -- a list of space and/or comma 22# separated languages to use on certain networks/channels. 23# Example: 24# /set spellcheck_languages netA/#chan1/en_US, #chan2/fi_FI, netB/!chan3/pl_PL 25# will use en_US for #chan1 on network netA, fi_FI for #chan2 26# on every network, and pl_PL for !chan3 on network netB. 27# By default this setting is empty. 28# 29# spellcheck_default_language -- language to use in empty 30# windows, or when nothing from spellcheck_languages matches. 31# Defaults to 'en_US'. 32# 33# spellcheck_enabled [ON/OFF] -- self explaining. Sometimes 34# (like when pasting foreign-language text) you don't want 35# the script to spit out lots of suggestions, and turning it 36# off for a while is the easiest way. By default it's ON. 37# 38# 39# BUGS: 40# - won't catch all mistakes 41# - picking actual words from what you type is very kludgy, 42# you may occasionally see some leftovers like digits or punctuation 43# - works every time you press space or a dot (so won't work for 44# the last word before pressing enter, unless you're using dot 45# to finish your sentences) 46# - when you press space and realize that the word is wrong, 47# you can't tabcomplete to the suggestions right away - you need 48# to use backspace and then tabcomplete. With dot you get an extra 49# space after tabcompletion. 50# - all words will be marked and no suggestions given if 51# dictionary is missing (ie. wrong spellcheck_default_language) 52# - probably more, please report to $IRSSI{'contact'} 53# 54# 55# $Id: spellcheck.pl 5 2008-05-28 22:31:06Z shasta $ 56# 57 58use strict; 59use vars qw($VERSION %IRSSI); 60use Irssi 20070804; 61use Text::Aspell; 62 63$VERSION = '0.4'; 64%IRSSI = ( 65 authors => 'Jakub Jankowski', 66 contact => 'shasta@toxcorp.com', 67 name => 'Spellcheck', 68 description => 'Checks for spelling errors using Aspell.', 69 license => 'GPLv2', 70 url => 'http://toxcorp.com/irc/irssi/spellcheck/', 71); 72 73my %speller; 74 75sub spellcheck_setup 76{ 77 return if (exists $speller{$_[0]} && defined $speller{$_[0]}); 78 $speller{$_[0]} = Text::Aspell->new or return undef; 79 $speller{$_[0]}->set_option('lang', $_[0]) or return undef; 80 $speller{$_[0]}->set_option('sug-mode', 'fast') or return undef; 81 return 1; 82} 83 84# add_rest means "add (whatever you chopped from the word before 85# spellchecking it) to the suggestions returned" 86sub spellcheck_check_word 87{ 88 my ($lang, $word, $add_rest) = @_; 89 my $win = Irssi::active_win(); 90 my @suggestions = (); 91 92 # setup Text::Aspell for that lang if needed 93 if (!exists $speller{$lang} || !defined $speller{$lang}) 94 { 95 if (!spellcheck_setup($lang)) 96 { 97 $win->print("Error while setting up spellchecker for $lang"); 98 # don't change the message 99 return @suggestions; 100 } 101 } 102 103 # do the spellchecking 104 my ($stripped, $rest) = $word =~ /([^[:punct:][:digit:]]{2,})(.*)/; # HAX 105 # Irssi::print("Debug: stripped $word is '$stripped', rest is '$rest'"); 106 if (defined $stripped && !$speller{$lang}->check($stripped)) 107 { 108 push(@suggestions, $add_rest ? $_ . $rest : $_) for ($speller{$lang}->suggest($stripped)); 109 } 110 return @suggestions; 111} 112 113sub spellcheck_find_language 114{ 115 my ($network, $target) = @_; 116 return Irssi::settings_get_str('spellcheck_default_language') unless (defined $network && defined $target); 117 118 # support !channels correctly 119 $target = '!' . substr($target, 6) if ($target =~ /^\!/); 120 121 # lowercase net/chan 122 $network = lc($network); 123 $target = lc($target); 124 125 # possible settings: network/channel/lang or channel/lang 126 my @languages = split(/[ ,]/, Irssi::settings_get_str('spellcheck_languages')); 127 for my $langstr (@languages) 128 { 129 # strip trailing slashes 130 $langstr =~ s=/+$==; 131 # Irssi::print("Debug: checking network $network target $target against langstr $langstr"); 132 my ($s1, $s2, $s3) = split(/\//, $langstr, 3); 133 my ($t, $c, $l); 134 if (defined $s3 && $s3 ne '') 135 { 136 # network/channel/lang 137 $t = lc($s1); $c = lc($s2); $l = $s3; 138 } 139 else 140 { 141 # channel/lang 142 $c = lc($s1); $l = $s2; 143 } 144 145 if ($c eq $target && (!defined $t || $t eq $network)) 146 { 147 # Irssi::print("Debug: language found: $l"); 148 return $l; 149 } 150 } 151 152 # Irssi::print("Debug: language not found, using default"); 153 # no match, use defaults 154 return Irssi::settings_get_str('spellcheck_default_language'); 155} 156 157sub spellcheck_key_pressed 158{ 159 my ($key) = @_; 160 my $win = Irssi::active_win(); 161 162 # I know no way to *mark* misspelled words in the input line, 163 # that's why there's no spellcheck_print_suggestions - 164 # because printing suggestions is our only choice. 165 return unless Irssi::settings_get_bool('spellcheck_enabled'); 166 167 # don't bother unless pressed key is space or dot 168 return unless (chr $key eq ' ' or chr $key eq '.'); 169 170 # get current inputline 171 my $inputline = Irssi::parse_special('$L'); 172 173 # check if inputline starts with any of cmdchars 174 # we shouldn't spellcheck commands 175 my $cmdchars = Irssi::settings_get_str('cmdchars'); 176 my $cmdre = qr/^[$cmdchars]/; 177 return if ($inputline =~ $cmdre); 178 179 # get last bit from the inputline 180 my ($word) = $inputline =~ /\s*([^\s]+)$/; 181 182 # do not spellcheck urls 183 my $urlre = qr/(^[a-zA-Z]+:\/\/\S+)|(^www)/; 184 return if ($word =~ $urlre); 185 186 # find appropriate language for current window item 187 my $lang = spellcheck_find_language($win->{active_server}->{tag}, $win->{active}->{name}); 188 189 my @suggestions = spellcheck_check_word($lang, $word, 0); 190 # Irssi::print("Debug: spellcheck_check_word($word) returned array of " . scalar @suggestions); 191 return if (scalar @suggestions == 0); 192 193 # we found a mistake, print suggestions 194 $win->print("Suggestions for $word - " . join(", ", @suggestions)); 195} 196 197 198sub spellcheck_complete_word 199{ 200 my ($complist, $win, $word, $lstart, $wantspace) = @_; 201 202 return unless Irssi::settings_get_bool('spellcheck_enabled'); 203 204 # find appropriate language for the current window item 205 my $lang = spellcheck_find_language($win->{active_server}->{tag}, $win->{active}->{name}); 206 207 # add suggestions to the completion list 208 push(@$complist, spellcheck_check_word($lang, $word, 1)); 209} 210 211 212Irssi::settings_add_bool('spellcheck', 'spellcheck_enabled', 1); 213Irssi::settings_add_str( 'spellcheck', 'spellcheck_default_language', 'en_US'); 214Irssi::settings_add_str( 'spellcheck', 'spellcheck_languages', ''); 215 216Irssi::signal_add_first('gui key pressed', 'spellcheck_key_pressed'); 217Irssi::signal_add_last('complete word', 'spellcheck_complete_word'); 218