CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-gnureadline

The standard Python readline extension statically linked against the GNU readline library.

Pending
Overview
Eval results
Files

completion.mddocs/

Tab Completion

Programmable tab completion system with customizable word breaking and completion functions. The gnureadline completion system provides context-aware completion with full control over behavior and display.

Capabilities

Completion Function Management

Set up and manage the function that provides completion suggestions.

def set_completer(function):
    """
    Set the completion function.
    
    Parameters:
    - function: Completion function(text: str, state: int) -> str or None
               Should return the state-th completion for text, or None when done
    """

def get_completer():
    """
    Get the current completion function.
    
    Returns:
    callable or None: Current completion function
    """

Usage Examples

import gnureadline
import os

def file_completer(text: str, state: int) -> str:
    """Complete file and directory names."""
    if state == 0:
        # First call - generate list of matches
        if not hasattr(file_completer, 'matches'):
            file_completer.matches = []
        
        # Get directory to search in
        dirname = os.path.dirname(text) or '.'
        basename = os.path.basename(text)
        
        try:
            # Find matching files
            file_completer.matches = []
            for filename in os.listdir(dirname):
                if filename.startswith(basename):
                    full_path = os.path.join(dirname, filename)
                    if os.path.isdir(full_path):
                        file_completer.matches.append(filename + '/')
                    else:
                        file_completer.matches.append(filename)
        except OSError:
            file_completer.matches = []
    
    # Return the state-th match
    try:
        return file_completer.matches[state]
    except IndexError:
        return None

# Set up file completion
gnureadline.set_completer(file_completer)
gnureadline.parse_and_bind("tab: complete")

# Test the completer
current_completer = gnureadline.get_completer()
print(f"Current completer: {current_completer.__name__}")

Word Breaking Configuration

Control how readline splits the input into words for completion.

def set_completer_delims(string: str):
    """
    Set the word break characters for completion.
    
    Parameters:
    - string: Characters that separate words for completion
    """

def get_completer_delims() -> str:
    """
    Get the current word break characters for completion.
    
    Returns:
    str: Current word break characters
    """

Usage Examples

import gnureadline

# Default delimiters include space, tab, newline and many punctuation chars
default_delims = gnureadline.get_completer_delims()
print(f"Default delimiters: '{default_delims}'")

# Set custom delimiters for specific applications
# For shell-like completion, keep most punctuation as delimiters
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?')

# For Python identifier completion, use fewer delimiters
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?/')

# For path completion, don't break on forward slashes
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?')

print(f"Updated delimiters: '{gnureadline.get_completer_delims()}'")

Completion Context Information

Access information about the current completion context.

def get_completion_type() -> int:
    """
    Get the type of completion being attempted.
    
    Returns:
    int: Completion type constant
    """

def get_begidx() -> int:
    """
    Get the beginning index of the completion.
    
    Returns:
    int: Start index of text being completed
    """

def get_endidx() -> int:
    """
    Get the ending index of the completion.
    
    Returns:
    int: End index of text being completed
    """

Usage Examples

import gnureadline

def context_aware_completer(text: str, state: int) -> str:
    """Completer that uses context information."""
    if state == 0:
        # Get completion context
        begidx = gnureadline.get_begidx()
        endidx = gnureadline.get_endidx()
        completion_type = gnureadline.get_completion_type()
        line_buffer = gnureadline.get_line_buffer()
        
        print(f"\nCompletion context:")
        print(f"  Text: '{text}'")
        print(f"  Begin: {begidx}, End: {endidx}")
        print(f"  Type: {completion_type}")
        print(f"  Buffer: '{line_buffer}'")
        print(f"  Before: '{line_buffer[:begidx]}'")
        print(f"  After: '{line_buffer[endidx:]}'")
        
        # Generate completions based on context
        before_text = line_buffer[:begidx].strip()
        
        if not before_text:
            # At beginning of line - complete commands
            context_aware_completer.matches = [
                cmd for cmd in ['help', 'list', 'show', 'quit', 'exit']
                if cmd.startswith(text)
            ]
        elif before_text.endswith('--'):
            # Complete long options
            context_aware_completer.matches = [
                opt for opt in ['--help', '--version', '--verbose', '--quiet']
                if opt.startswith(text)
            ]
        else:
            # Complete filenames
            import os
            try:
                context_aware_completer.matches = [
                    f for f in os.listdir('.')
                    if f.startswith(text)
                ]
            except OSError:
                context_aware_completer.matches = []
    
    try:
        return context_aware_completer.matches[state]
    except (IndexError, AttributeError):
        return None

gnureadline.set_completer(context_aware_completer)
gnureadline.parse_and_bind("tab: complete")

Advanced Completion Examples

Command-Specific Completion

import gnureadline
import shlex

class CommandCompleter:
    def __init__(self):
        self.commands = {
            'ls': self._complete_files,
            'cd': self._complete_directories,
            'cat': self._complete_files,
            'grep': self._complete_grep,
            'help': self._complete_help_topics,
        }
        
        self.help_topics = ['commands', 'completion', 'history', 'configuration']
    
    def complete(self, text: str, state: int) -> str:
        """Main completion entry point."""
        if state == 0:
            line_buffer = gnureadline.get_line_buffer()
            begidx = gnureadline.get_begidx()
            
            # Parse the command line
            try:
                tokens = shlex.split(line_buffer[:begidx])
            except ValueError:
                tokens = line_buffer[:begidx].split()
            
            if not tokens:
                # Complete command names
                self.matches = [cmd for cmd in self.commands.keys() if cmd.startswith(text)]
            else:
                command = tokens[0]
                if command in self.commands:
                    # Command-specific completion
                    self.matches = self.commands[command](text, tokens)
                else:
                    # Default to file completion
                    self.matches = self._complete_files(text, tokens)
        
        try:
            return self.matches[state]
        except (IndexError, AttributeError):
            return None
    
    def _complete_files(self, text: str, tokens: list) -> list:
        """Complete file names."""
        import os
        import glob
        
        if text:
            pattern = text + '*'
        else:
            pattern = '*'
        
        matches = glob.glob(pattern)
        return [match + ('/' if os.path.isdir(match) else '') for match in matches]
    
    def _complete_directories(self, text: str, tokens: list) -> list:
        """Complete directory names only."""
        import os
        import glob
        
        pattern = text + '*' if text else '*'
        matches = glob.glob(pattern)
        return [match + '/' for match in matches if os.path.isdir(match)]
    
    def _complete_grep(self, text: str, tokens: list) -> list:
        """Complete grep command with patterns and files."""
        if len(tokens) < 2:
            # First argument - could be a pattern or option
            if text.startswith('-'):
                return [opt for opt in ['-i', '-v', '-r', '-n', '-l'] if opt.startswith(text)]
            else:
                # Return some common patterns
                patterns = ['error', 'warning', 'TODO', 'FIXME', 'import', 'def ', 'class ']
                return [p for p in patterns if p.startswith(text)]
        else:
            # Additional arguments - complete files
            return self._complete_files(text, tokens)
    
    def _complete_help_topics(self, text: str, tokens: list) -> list:
        """Complete help topics."""
        return [topic for topic in self.help_topics if topic.startswith(text)]

# Set up the command completer
completer = CommandCompleter()
gnureadline.set_completer(completer.complete)
gnureadline.parse_and_bind("tab: complete")

# Configure word delimiters for shell-like behavior
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?')

Python Code Completion

import gnureadline
import keyword
import builtins

class PythonCompleter:
    def __init__(self):
        self.namespace = {
            **builtins.__dict__,
            **globals(),
            **locals()
        }
    
    def complete(self, text: str, state: int) -> str:
        """Complete Python identifiers and keywords."""
        if state == 0:
            self.matches = []
            
            # Get the current line context
            line = gnureadline.get_line_buffer()
            begidx = gnureadline.get_begidx()
            
            # Simple completion logic
            if '.' in text:
                # Attribute completion
                parts = text.split('.')
                obj_name = '.'.join(parts[:-1])
                attr_prefix = parts[-1]
                
                try:
                    obj = eval(obj_name, self.namespace)
                    attrs = [attr for attr in dir(obj) 
                            if not attr.startswith('_') and attr.startswith(attr_prefix)]
                    self.matches = [f"{obj_name}.{attr}" for attr in attrs]
                except:
                    self.matches = []
            else:
                # Name completion
                candidates = []
                
                # Python keywords
                candidates.extend(keyword.kwlist)
                
                # Built-in functions and types
                candidates.extend(dir(builtins))
                
                # Names in current namespace
                candidates.extend(self.namespace.keys())
                
                # Filter matches
                self.matches = [name for name in candidates 
                              if name.startswith(text) and not name.startswith('_')]
                
                # Sort matches
                self.matches.sort()
        
        try:
            return self.matches[state]
        except (IndexError, AttributeError):
            return None
    
    def update_namespace(self, new_namespace: dict):
        """Update the completion namespace."""
        self.namespace.update(new_namespace)

# Example usage in a Python REPL
python_completer = PythonCompleter()
gnureadline.set_completer(python_completer.complete)
gnureadline.parse_and_bind("tab: complete")

# For Python code, use different delimiters
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?/')

# Update namespace as variables are defined
python_completer.update_namespace({'my_variable': 42, 'my_function': lambda x: x})

Completion Display Customization

For advanced completion display options, see the Hook Functions documentation, specifically the set_completion_display_matches_hook function which allows customization of how completion matches are presented to the user.

Install with Tessl CLI

npx tessl i tessl/pypi-gnureadline

docs

completion.md

history.md

hooks.md

index.md

line-editing.md

utilities.md

tile.json