Aha2Y

Untitled

Apr 16th, 2012
298
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.09 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. ######################################################################
  3. # Copyright (c) 2011 by Pascal Wittmann <mail@pascal-wittmann.de>
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. # Marked Parts are from Wojciech Muła <wojciech_mula@poczta.onet.pl>
  19. # and are licensed under BSD and are avaliable at
  20. # http://0x80.pl/proj/aspell-python/
  21. ########################################################################
  22.  
  23. # INSTALLTION
  24. # After copying this file into your python plugin directory, start weechat
  25. # load the script and follow futher instructions calling
  26. #    /help correction_completion
  27. # You can find these instructions as markdown on
  28. #    https://github.com/pSub/weechat-correction-completion/blob/master/README.md
  29. # too.
  30.  
  31. try:
  32.     import ctypes
  33.     import ctypes.util
  34. except ImportError:
  35.     print "This script depends on ctypes"
  36.  
  37. try:
  38.     import weechat as w
  39. except ImportError:
  40.     print "This script must be run under WeeChat."
  41.     print "Get WeeChat now at: http://www.weechat.org/"
  42.  
  43. SCRIPT_NAME    = "correction_completion"
  44. SCRIPT_AUTHOR  = "Pascal Wittmann <mail@pascal-wittmann.de>"
  45. SCRIPT_VERSION = "0.2.3"
  46. SCRIPT_LICENSE = "GPL3"
  47. SCRIPT_DESC    = "Provides a completion for 's/typo/correct'"
  48. SCRIPT_COMMAND = "correction_completion"
  49.  
  50. # Default Options
  51. # Your can use all aspell options listed on
  52. # http://aspell.net/man-html/The-Options.html
  53. settings = {
  54.         'lang' : 'en',
  55. }
  56.  
  57. # The Bunch Class is from
  58. # http://code.activestate.com/recipes/52308/
  59. class Bunch:
  60.     def __init__(self, **kwds):
  61.         self.__dict__.update(kwds)
  62.  
  63. def completion(data, completion_item, buffer, completion):
  64.     if state.used == True:
  65.         return w.WEECHAT_RC_OK
  66.     else:
  67.         state.used = True
  68.  
  69.     # Current cursor position
  70.     pos = w.buffer_get_integer(buffer, 'input_pos')
  71.  
  72.     # Current input string
  73.     input = w.buffer_get_string(buffer, 'input')
  74.    
  75.     fst = input.find("s/")
  76.     snd = input.find("/", fst + 2)
  77.  
  78.     # Check for typo or suggestion completion
  79.     if pos > 2 and fst >= 0 and fst < pos:
  80.         if snd >= 0 and snd < pos:
  81.           complete_replacement(pos, input, buffer)
  82.         else:
  83.           complete_typo(pos, input, buffer)
  84.  
  85.     state.used = False
  86.     return w.WEECHAT_RC_OK
  87.  
  88. def complete_typo(pos, input, buffer):
  89.     # Assume that typo changes when doing a completion
  90.     state.curRepl = -1
  91.  
  92.     # Get the text of the current buffer
  93.     list = []
  94.     infolist = w.infolist_get('buffer_lines', buffer, '')
  95.     while w.infolist_next(infolist):
  96.         list.append(stripcolor(w.infolist_string(infolist, 'message')))
  97.     w.infolist_free(infolist)
  98.  
  99.     # Generate a list of words
  100.     text = (' '.join(list)).split(' ')
  101.  
  102.     # Remove duplicate elements
  103.     text = unify(text)
  104.  
  105.     # Sort by alphabet and length
  106.     text.sort(key=lambda item: (item, -len(item)))
  107.    
  108.     i = iter(text)
  109.    
  110.     # Get index of last occurence of "s/" befor cursor position
  111.     n = input.rfind("s/", 0, pos)
  112.  
  113.     # Get substring and search the replacement
  114.     substr = input[n+2:pos]
  115.     replace = search((lambda word : word.startswith(substr)), i)
  116.    
  117.     # If no replacement found, display substring
  118.     if replace == "":
  119.       replace = substr
  120.    
  121.     # If substring perfectly matched take next replacement
  122.     if replace == substr:
  123.       try:
  124.         replace = next(i)
  125.       except StopIteration:
  126.         replace = substr
  127.  
  128.     changeInput(substr, replace, input, pos, buffer)
  129.  
  130. def complete_replacement(pos, input, buffer):
  131.     # Start Positions
  132.     n = input.rfind("s/", 0, pos)
  133.     m = input.rfind("/", n + 2, pos)
  134.    
  135.     repl = input[m + 1 : pos]
  136.     typo = input[n + 2 : m]
  137.    
  138.     # Only query new suggestions, when typo changed
  139.     if state.curRepl == -1 or typo != state.curTypo:
  140.       state.suggestions = suggest(typo)
  141.       state.curTypo = typo
  142.  
  143.     if len(state.suggestions) == 0:
  144.       return
  145.  
  146.     # Start at begining when reached end of suggestions
  147.     if state.curRepl == len(state.suggestions) - 1:
  148.       state.curRepl = -1
  149.    
  150.     # Take next suggestion
  151.     state.curRepl += 1
  152.    
  153.     # Put suggestion into the input
  154.     changeInput(repl, state.suggestions[state.curRepl], input, pos, buffer)
  155.  
  156. def changeInput(search, replace, input, pos, buffer):
  157.     # Put the replacement into the input
  158.     n = len(search)
  159.     input = '%s%s%s' %(input[:pos-n], replace, input[pos:])
  160.     w.buffer_set(buffer, 'input', input)
  161.     w.buffer_set(buffer, 'input_pos', str(pos - n + len(replace)))
  162.  
  163. def stripcolor(string):
  164.     return w.string_remove_color(string, '')
  165.  
  166. def search(p, i):
  167.     # Search for item matching the predicate p
  168.     while True:
  169.       try:
  170.         item = next(i)
  171.         if p(item):
  172.           return item
  173.       except StopIteration:
  174.         return ""
  175.  
  176. def unify(list):
  177.     # Remove duplicate elements from a list
  178.     checked = []
  179.     for e in list:
  180.       if e not in checked:
  181.         checked.append(e)
  182.     return checked
  183.  
  184. # Parts are from Wojciech Muła
  185. def suggest(word):
  186.     if type(word) is str:
  187.       suggestions = aspell.aspell_speller_suggest(
  188.                       speller,
  189.                       word.encode(),
  190.                       len(word))
  191.       elements = aspell.aspell_word_list_elements(suggestions)
  192.       list = []
  193.       while True:
  194.           wordptr = aspell.aspell_string_enumeration_next(elements)
  195.           if not wordptr:
  196.               break;
  197.           else:
  198.               word = ctypes.c_char_p(wordptr)
  199.               list.append(str(word.value))
  200.       aspell.delete_aspell_string_enumeration(elements)
  201.       return list
  202.     else:
  203.       raise TypeError("String expected")
  204.  
  205. def load_config(data = "", option = "", value = ""):
  206.     global speller
  207.     config = aspell.new_aspell_config()
  208.  
  209.     for option, default in settings.iteritems():
  210.         if not w.config_is_set_plugin(option):
  211.           w.config_set_plugin(option, default)
  212.         value = w.config_get_plugin(option)
  213.         if not aspell.aspell_config_replace(
  214.                         config,
  215.                         option.encode(),
  216.                         value.encode()):
  217.           raise Exception("Failed to replace config entry")
  218.  
  219.     # Error checking is from Wojciech Muła
  220.     possible_error = aspell.new_aspell_speller(config)
  221.     aspell.delete_aspell_config(config)
  222.     if aspell.aspell_error_number(possible_error) != 0:
  223.       aspell.delete_aspell_can_have_error(possible_error)
  224.       raise Exception("Couldn't create speller")
  225.     speller = aspell.to_aspell_speller(possible_error)
  226.     return w.WEECHAT_RC_OK
  227.  
  228. if w.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""):
  229.     # Saving the current completion state
  230.     state = Bunch(used = False, curTypo = '', curRepl = -1, suggestions = [])
  231.  
  232.     # Use ctypes to access the apsell library
  233.     aspell = ctypes.CDLL(ctypes.util.find_library('aspell'))
  234.     speller = 0
  235.    
  236.     # Load configuration
  237.     load_config()
  238.  
  239.     template = 'correction_completion'
  240.    
  241.     # Register completion hook
  242.     w.hook_completion(template, "Completes after 's/' with words from buffer",
  243.             'completion', '')
  244.  
  245.     # Register hook to update config when option is changed with /set
  246.     w.hook_config("plugins.var.python." + SCRIPT_NAME + ".*", "load_config", "")
  247.  
  248.     # Register help command
  249.     w.hook_command(SCRIPT_COMMAND, SCRIPT_DESC, "",
  250. """Usage:
  251. If you want to correct yourself, you often do this using the
  252. expression 's/typo/correct'. This plugin allows you to complete the
  253. first part (the typo) by pressing *Tab*. The words from the actual
  254. buffer are used to complet this part. If the word can be perfectly
  255. matched the next word in alphabetical order is shown.
  256.  
  257. The second part (the correction) can also be completed. Just press
  258. *Tab* after the slash and the best correction for the typo is fetched from aspell.
  259. If you press *Tab* again, it shows the next suggestion.
  260. The lanuage used for suggestions can be set with the option
  261.  
  262.  plugins.var.python.correction_completion.lang
  263.  
  264. The aspell language pack must be installed for this language.
  265.  
  266. Setup:
  267. Add the template %%(%(completion)s) to the default completion template.
  268. The best way to set the template is to use the iset-plugin¹, because you can see
  269. there the current value before changing it. Of course you can also use the
  270. standard /set-command e.g.
  271.  
  272.  /set weechat.completion.default_template "%%(nicks)|%%(irc_channels)|%%(%(completion)s)"
  273.  
  274. Footnotes:
  275. ¹ http://weechat.org/scripts/source/stable/iset.pl/
  276. """
  277. %dict(completion=template), '', '', '')
Add Comment
Please, Sign In to add comment