old_contents: from __future__ import print_function
  import hexchat
  
  __module_name__ = "PythonTabComplete"
  __module_author__ = "TingPing, FichteFoll"
  __module_version__ = "0.2"
  __module_description__ = "Tab-completes module attributes in Interactive Console"
  
  last_index = None
  last_completes = None
  last_text = None
  last_pos = None
  
  exec_scope = {}
  
  
  def keypress_cb(word, word_eol, userdata):
  	global last_index
  	global last_completes
  	global last_text
  	global last_pos
  
  	if not word[0] == '65289':  # Tab
  		return
  	if not hexchat.get_info('channel') == '>>python<<':
  		return
  
  	text = hexchat.get_info('inputbox')
  	pos = hexchat.get_prefs('state_cursor')
  	if not text:
  		return
  
  	base = text[:pos].split(' ')[-1]
  	module, _, prefix = base.rpartition('.')
  	if not module:
  		# can not dir() the console's interpreter state sadly
  		return
  
  	if last_text != text or last_pos != pos:
  		# new completion context
  		try:
  			exec('import {}'.format(module), exec_scope)  # Has to be imported to dir() it
  			completions = eval('dir({})'.format(module), exec_scope)
  		except (NameError, SyntaxError, ImportError):
  			return
  		completions = [c for c in completions if c.startswith(prefix)]
  		if not completions:
  			return
  		index = 0
  	else:
  		# same context, insert next completion
  		completions = last_completes
  		index = (last_index + 1) % len(completions)
  
  	complete_text = completions[index]
  
  	new_text = text[:pos - len(prefix)] + complete_text + text[pos:]
  	new_pos = pos - len(prefix) + len(complete_text)
  
  	hexchat.command('settext {}'.format(new_text))
  	hexchat.command('setcursor {}'.format(new_pos))
  
  	last_index = index
  	last_completes = completions
  	last_text = new_text
  	last_pos = new_pos
  
  
  def unload_cb(userdata):
  	print(__module_name__, 'version', __module_version__, 'unloaded.')
  
  
  hexchat.hook_print('Key Press', keypress_cb)
  hexchat.hook_unload(unload_cb)
  print(__module_name__, 'version', __module_version__, 'loaded.')
  
new_contents: from __future__ import print_function
  import hexchat
  
  __module_name__ = "PythonTabComplete"
  __module_author__ = "TingPing, FichteFoll"
  __module_version__ = "0.3"
  __module_description__ = "Tab-completes module attributes in Interactive Console"
  
  last_index = None
  last_completes = None
  last_text = None
  last_pos = None
  
  exec_scope = {}
  
  
  def keypress_cb(word, word_eol, userdata):
  	global last_index
  	global last_completes
  	global last_text
  	global last_pos
  
  	if not word[0] == '65289':  # Tab
  		return
  	if not hexchat.get_info('channel') == '>>python<<':
  		return
  	shift_key_is_down = bool(int(word[1]) & 1)  # check if shift modifier is hold
  
  	text = hexchat.get_info('inputbox')
  	pos = hexchat.get_prefs('state_cursor')
  	if not text:
  		return
  
  	base = text[:pos].split(' ')[-1]
  	module, _, prefix = base.rpartition('.')
  	if not module:
  		# can not dir() the console's interpreter state sadly
  		return
  
  	if last_text != text or last_pos != pos:
  		# new completion context
  		try:
  			exec('import {}'.format(module), exec_scope)  # Has to be imported to dir() it
  			completions = eval('dir({})'.format(module), exec_scope)
  		except (NameError, SyntaxError, ImportError):
  			return
  		completions = [c for c in completions if c.startswith(prefix)]
  		if not completions:
  			return
  		index = 0
  	else:
  		# same context, insert next completion
  		completions = last_completes
  		direction = 1 if not shift_key_is_down else -1
  		index = (last_index + direction) % len(completions)
  
  	complete_text = completions[index]
  
  	new_text = text[:pos - len(prefix)] + complete_text + text[pos:]
  	new_pos = pos - len(prefix) + len(complete_text)
  
  	hexchat.command('settext {}'.format(new_text))
  	hexchat.command('setcursor {}'.format(new_pos))
  
  	last_index = index
  	last_completes = completions
  	last_text = new_text
  	last_pos = new_pos
  
  
  def unload_cb(userdata):
  	print(__module_name__, 'version', __module_version__, 'unloaded.')
  
  
  hexchat.hook_print('Key Press', keypress_cb)
  hexchat.hook_unload(unload_cb)
  print(__module_name__, 'version', __module_version__, 'loaded.')
  
subject: Allow cycling backwards if shift is hold