Add keyboard shortcuts, configuration management, and command processing
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
Command processor for Ubuntu Voice Typing.
|
||||
|
||||
This module processes text commands from speech recognition, such as
|
||||
"new line", "period", etc.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CommandProcessor:
|
||||
"""
|
||||
Processes text commands in speech recognition results.
|
||||
|
||||
This class handles special commands like "new line", "period",
|
||||
"delete that", etc.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the command processor."""
|
||||
# Map of command phrases to their actions
|
||||
self.text_commands = {
|
||||
# Line commands
|
||||
"new line": "\n",
|
||||
"new paragraph": "\n\n",
|
||||
|
||||
# Punctuation
|
||||
"period": ".",
|
||||
"full stop": ".",
|
||||
"comma": ",",
|
||||
"question mark": "?",
|
||||
"exclamation mark": "!",
|
||||
"exclamation point": "!",
|
||||
"semicolon": ";",
|
||||
"colon": ":",
|
||||
"dash": "-",
|
||||
"hyphen": "-",
|
||||
"underscore": "_",
|
||||
"quote": "\"",
|
||||
"single quote": "'",
|
||||
"open parenthesis": "(",
|
||||
"close parenthesis": ")",
|
||||
"open bracket": "[",
|
||||
"close bracket": "]",
|
||||
"open brace": "{",
|
||||
"close brace": "}",
|
||||
}
|
||||
|
||||
# Special action commands that don't directly map to text
|
||||
self.action_commands = {
|
||||
"delete that": "delete_last",
|
||||
"scratch that": "delete_last",
|
||||
"undo": "undo",
|
||||
"redo": "redo",
|
||||
"select all": "select_all",
|
||||
"select line": "select_line",
|
||||
"select word": "select_word",
|
||||
"select paragraph": "select_paragraph",
|
||||
"cut": "cut",
|
||||
"copy": "copy",
|
||||
"paste": "paste",
|
||||
}
|
||||
|
||||
# Formatting commands that modify the next word
|
||||
self.format_commands = {
|
||||
"capitalize": "capitalize_next",
|
||||
"uppercase": "capitalize_next",
|
||||
"all caps": "uppercase_next",
|
||||
"lowercase": "lowercase_next",
|
||||
"no spaces": "no_spaces_next",
|
||||
}
|
||||
|
||||
# Active format modifiers
|
||||
self.active_formats = set()
|
||||
|
||||
# Compile regex patterns for faster matching
|
||||
self._compile_patterns()
|
||||
|
||||
def _compile_patterns(self):
|
||||
"""Compile regex patterns for command matching."""
|
||||
# Create regex pattern for text commands
|
||||
text_cmd_pattern = r'\b(' + '|'.join(re.escape(cmd) for cmd in self.text_commands.keys()) + r')\b'
|
||||
self.text_cmd_regex = re.compile(text_cmd_pattern, re.IGNORECASE)
|
||||
|
||||
# Create regex pattern for action commands
|
||||
action_cmd_pattern = r'\b(' + '|'.join(re.escape(cmd) for cmd in self.action_commands.keys()) + r')\b'
|
||||
self.action_cmd_regex = re.compile(action_cmd_pattern, re.IGNORECASE)
|
||||
|
||||
# Create regex pattern for format commands
|
||||
format_cmd_pattern = r'\b(' + '|'.join(re.escape(cmd) for cmd in self.format_commands.keys()) + r')\b'
|
||||
self.format_cmd_regex = re.compile(format_cmd_pattern, re.IGNORECASE)
|
||||
|
||||
def process_text(self, text: str) -> Tuple[str, List[str]]:
|
||||
"""
|
||||
Process text commands in the recognized text.
|
||||
|
||||
Args:
|
||||
text: The recognized text to process
|
||||
|
||||
Returns:
|
||||
Tuple of (processed_text, actions)
|
||||
- processed_text: The text with commands replaced
|
||||
- actions: List of special actions to perform
|
||||
"""
|
||||
if not text:
|
||||
return "", []
|
||||
|
||||
logger.debug(f"Processing commands in text: {text}")
|
||||
|
||||
# Convert to lowercase for easier matching
|
||||
lower_text = text.lower()
|
||||
processed_text = text
|
||||
actions = []
|
||||
|
||||
# Process action commands first (delete that, undo, etc.)
|
||||
action_matches = self.action_cmd_regex.findall(lower_text)
|
||||
for match in action_matches:
|
||||
action = self.action_commands[match]
|
||||
actions.append(action)
|
||||
# Remove the command from the text
|
||||
processed_text = re.sub(r'\b' + re.escape(match) + r'\b', '', processed_text, flags=re.IGNORECASE)
|
||||
|
||||
# Process format commands
|
||||
format_matches = self.format_cmd_regex.findall(lower_text)
|
||||
for match in format_matches:
|
||||
action = self.format_commands[match]
|
||||
self.active_formats.add(action)
|
||||
# Remove the command from the text
|
||||
processed_text = re.sub(r'\b' + re.escape(match) + r'\b', '', processed_text, flags=re.IGNORECASE)
|
||||
|
||||
# Apply active format modifiers to the next word
|
||||
if self.active_formats:
|
||||
# Find the next word
|
||||
word_match = re.search(r'\b(\w+)\b', processed_text)
|
||||
if word_match:
|
||||
word = word_match.group(1)
|
||||
start, end = word_match.span(1)
|
||||
|
||||
# Apply formatting
|
||||
formatted_word = word
|
||||
for format_type in self.active_formats:
|
||||
if format_type == "capitalize_next":
|
||||
formatted_word = formatted_word.capitalize()
|
||||
elif format_type == "uppercase_next":
|
||||
formatted_word = formatted_word.upper()
|
||||
elif format_type == "lowercase_next":
|
||||
formatted_word = formatted_word.lower()
|
||||
elif format_type == "no_spaces_next":
|
||||
# This will be applied when combining with the next word
|
||||
pass
|
||||
|
||||
# Replace the word in the text
|
||||
processed_text = processed_text[:start] + formatted_word + processed_text[end:]
|
||||
|
||||
# Clear active formats
|
||||
self.active_formats.clear()
|
||||
|
||||
# Finally, process text commands (period, comma, etc.)
|
||||
def replace_command(match):
|
||||
cmd = match.group(0).lower()
|
||||
replacement = self.text_commands.get(cmd, "")
|
||||
return replacement
|
||||
|
||||
processed_text = self.text_cmd_regex.sub(replace_command, processed_text)
|
||||
|
||||
# Clean up multiple spaces
|
||||
processed_text = re.sub(r'\s+', ' ', processed_text).strip()
|
||||
|
||||
return processed_text, actions
|
||||
Reference in New Issue
Block a user