324 lines
9.9 KiB
Python
324 lines
9.9 KiB
Python
#!/usr/bin/env python3
|
||||
|
|
"""
|
|||
|
|
Claude Filter Vim Plugin - Automatic Installation Script
|
|||
|
|
|
|||
|
|
Detects Vim/Neovim installation and installs the plugin with proper handling
|
|||
|
|
for existing installations (updates/overrides).
|
|||
|
|
|
|||
|
|
Features:
|
|||
|
|
- Auto-detects Vim vs Neovim
|
|||
|
|
- Auto-detects config directory
|
|||
|
|
- Installs both Lua (Neovim) and VimScript (Vim) versions
|
|||
|
|
- Handles existing installations (updates)
|
|||
|
|
- Cross-platform (Linux, macOS, Windows)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
import shutil
|
|||
|
|
import argparse
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PluginInstaller:
|
|||
|
|
def __init__(self, verbose=False, override=False):
|
|||
|
|
self.verbose = verbose
|
|||
|
|
self.override = override
|
|||
|
|
self.installed = []
|
|||
|
|
self.errors = []
|
|||
|
|
|
|||
|
|
def log(self, message, level="INFO"):
|
|||
|
|
"""Log messages if verbose mode is enabled"""
|
|||
|
|
if self.verbose or level in ["ERROR", "SUCCESS"]:
|
|||
|
|
prefix = "✅" if level == "SUCCESS" else "❌" if level == "ERROR" else "ℹ️"
|
|||
|
|
print(f"{prefix} [{level}] {message}")
|
|||
|
|
|
|||
|
|
def check_editor_installed(self):
|
|||
|
|
"""Check which editors are installed"""
|
|||
|
|
editors = {}
|
|||
|
|
|
|||
|
|
# Check Neovim
|
|||
|
|
if shutil.which("nvim"):
|
|||
|
|
editors["neovim"] = True
|
|||
|
|
self.log("Neovim detected")
|
|||
|
|
else:
|
|||
|
|
editors["neovim"] = False
|
|||
|
|
self.log("Neovim not found", "WARN")
|
|||
|
|
|
|||
|
|
# Check Vim
|
|||
|
|
if shutil.which("vim"):
|
|||
|
|
editors["vim"] = True
|
|||
|
|
self.log("Vim detected")
|
|||
|
|
else:
|
|||
|
|
editors["vim"] = False
|
|||
|
|
self.log("Vim not found", "WARN")
|
|||
|
|
|
|||
|
|
return editors
|
|||
|
|
|
|||
|
|
def get_neovim_config_dir(self):
|
|||
|
|
"""Get Neovim config directory (works on all platforms)"""
|
|||
|
|
# Check XDG_CONFIG_HOME first (Linux/macOS)
|
|||
|
|
xdg_config = os.environ.get("XDG_CONFIG_HOME")
|
|||
|
|
if xdg_config:
|
|||
|
|
nvim_dir = Path(xdg_config) / "nvim"
|
|||
|
|
if nvim_dir.exists():
|
|||
|
|
return nvim_dir
|
|||
|
|
|
|||
|
|
# Check standard locations
|
|||
|
|
home = Path.home()
|
|||
|
|
|
|||
|
|
# macOS
|
|||
|
|
if sys.platform == "darwin":
|
|||
|
|
nvim_dir = home / ".config" / "nvim"
|
|||
|
|
if nvim_dir.exists():
|
|||
|
|
return nvim_dir
|
|||
|
|
|
|||
|
|
# Linux
|
|||
|
|
if sys.platform.startswith("linux"):
|
|||
|
|
nvim_dir = home / ".config" / "nvim"
|
|||
|
|
if nvim_dir.exists():
|
|||
|
|
return nvim_dir
|
|||
|
|
|
|||
|
|
# Windows
|
|||
|
|
if sys.platform == "win32":
|
|||
|
|
# Try AppData first
|
|||
|
|
appdata = os.environ.get("APPDATA")
|
|||
|
|
if appdata:
|
|||
|
|
nvim_dir = Path(appdata) / "nvim"
|
|||
|
|
if nvim_dir.exists():
|
|||
|
|
return nvim_dir
|
|||
|
|
|
|||
|
|
# Try local AppData
|
|||
|
|
local_appdata = os.environ.get("LOCALAPPDATA")
|
|||
|
|
if local_appdata:
|
|||
|
|
nvim_dir = Path(local_appdata) / "nvim"
|
|||
|
|
if nvim_dir.exists():
|
|||
|
|
return nvim_dir
|
|||
|
|
|
|||
|
|
# Fallback: Create in ~/.config/nvim
|
|||
|
|
return home / ".config" / "nvim"
|
|||
|
|
|
|||
|
|
def get_vim_config_dir(self):
|
|||
|
|
"""Get Vim config directory"""
|
|||
|
|
home = Path.home()
|
|||
|
|
|
|||
|
|
# Check for .vim directory
|
|||
|
|
vim_dir = home / ".vim"
|
|||
|
|
if vim_dir.exists():
|
|||
|
|
return vim_dir
|
|||
|
|
|
|||
|
|
# Windows: check vimfiles
|
|||
|
|
if sys.platform == "win32":
|
|||
|
|
vimfiles = home / "vimfiles"
|
|||
|
|
if vimfiles.exists():
|
|||
|
|
return vimfiles
|
|||
|
|
|
|||
|
|
# Default to .vim
|
|||
|
|
return vim_dir
|
|||
|
|
|
|||
|
|
def install_neovim_plugin(self):
|
|||
|
|
"""Install Lua version for Neovim"""
|
|||
|
|
self.log("Installing Neovim plugin...")
|
|||
|
|
|
|||
|
|
nvim_dir = self.get_neovim_config_dir()
|
|||
|
|
plugin_dir = nvim_dir / "plugin"
|
|||
|
|
|
|||
|
|
# Create directory if it doesn't exist
|
|||
|
|
plugin_dir.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
# Source and destination
|
|||
|
|
source = Path(__file__).parent / "plugin" / "claude_filter.lua"
|
|||
|
|
destination = plugin_dir / "claude_filter.lua"
|
|||
|
|
|
|||
|
|
if not source.exists():
|
|||
|
|
self.log(f"Source file not found: {source}", "ERROR")
|
|||
|
|
self.errors.append(f"Neovim: Source file not found")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# Check if already installed
|
|||
|
|
if destination.exists():
|
|||
|
|
if not self.override:
|
|||
|
|
self.log(f"Already installed at {destination}")
|
|||
|
|
self.log("Use --override to update")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
self.log(f"Removing existing: {destination}")
|
|||
|
|
destination.unlink()
|
|||
|
|
|
|||
|
|
# Copy file
|
|||
|
|
try:
|
|||
|
|
shutil.copy2(source, destination)
|
|||
|
|
self.log(f"✅ Installed to {destination}", "SUCCESS")
|
|||
|
|
self.installed.append(("Neovim", destination))
|
|||
|
|
return True
|
|||
|
|
except Exception as e:
|
|||
|
|
self.log(f"Failed to copy: {e}", "ERROR")
|
|||
|
|
self.errors.append(f"Neovim: {str(e)}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def install_vim_plugin(self):
|
|||
|
|
"""Install VimScript version for Vim"""
|
|||
|
|
self.log("Installing Vim plugin...")
|
|||
|
|
|
|||
|
|
vim_dir = self.get_vim_config_dir()
|
|||
|
|
plugin_dir = vim_dir / "plugin"
|
|||
|
|
|
|||
|
|
# Create directory if it doesn't exist
|
|||
|
|
plugin_dir.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
# Source and destination
|
|||
|
|
source = Path(__file__).parent / "plugin" / "claude_filter.vim"
|
|||
|
|
destination = plugin_dir / "claude_filter.vim"
|
|||
|
|
|
|||
|
|
if not source.exists():
|
|||
|
|
self.log(f"Source file not found: {source}", "ERROR")
|
|||
|
|
self.errors.append(f"Vim: Source file not found")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# Check if already installed
|
|||
|
|
if destination.exists():
|
|||
|
|
if not self.override:
|
|||
|
|
self.log(f"Already installed at {destination}")
|
|||
|
|
self.log("Use --override to update")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
self.log(f"Removing existing: {destination}")
|
|||
|
|
destination.unlink()
|
|||
|
|
|
|||
|
|
# Copy file
|
|||
|
|
try:
|
|||
|
|
shutil.copy2(source, destination)
|
|||
|
|
self.log(f"✅ Installed to {destination}", "SUCCESS")
|
|||
|
|
self.installed.append(("Vim", destination))
|
|||
|
|
return True
|
|||
|
|
except Exception as e:
|
|||
|
|
self.log(f"Failed to copy: {e}", "ERROR")
|
|||
|
|
self.errors.append(f"Vim: {str(e)}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def install(self):
|
|||
|
|
"""Main installation logic"""
|
|||
|
|
self.log("=" * 60)
|
|||
|
|
self.log("Claude Filter Vim Plugin - Installer", "SUCCESS")
|
|||
|
|
self.log("=" * 60)
|
|||
|
|
|
|||
|
|
# Check what's installed
|
|||
|
|
editors = self.check_editor_installed()
|
|||
|
|
|
|||
|
|
if not any(editors.values()):
|
|||
|
|
self.log("Neither Vim nor Neovim found on this system", "ERROR")
|
|||
|
|
self.log("Install one of them first:")
|
|||
|
|
self.log(" - Neovim: https://neovim.io")
|
|||
|
|
self.log(" - Vim: https://www.vim.org")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
success = True
|
|||
|
|
|
|||
|
|
# Install for Neovim
|
|||
|
|
if editors["neovim"]:
|
|||
|
|
if not self.install_neovim_plugin():
|
|||
|
|
success = False
|
|||
|
|
|
|||
|
|
# Install for Vim
|
|||
|
|
if editors["vim"]:
|
|||
|
|
if not self.install_vim_plugin():
|
|||
|
|
success = False
|
|||
|
|
|
|||
|
|
# Summary
|
|||
|
|
self.log("=" * 60)
|
|||
|
|
if self.installed:
|
|||
|
|
self.log("Installation Summary:", "SUCCESS")
|
|||
|
|
for editor, path in self.installed:
|
|||
|
|
self.log(f" {editor}: {path}")
|
|||
|
|
|
|||
|
|
if self.errors:
|
|||
|
|
self.log("Errors encountered:", "ERROR")
|
|||
|
|
for error in self.errors:
|
|||
|
|
self.log(f" - {error}")
|
|||
|
|
|
|||
|
|
self.log("=" * 60)
|
|||
|
|
|
|||
|
|
# Usage instructions
|
|||
|
|
if self.installed:
|
|||
|
|
self.log("\n✅ Installation successful!\n", "SUCCESS")
|
|||
|
|
self.log("Usage:")
|
|||
|
|
self.log(" 1. Select text in visual mode (V + arrows)")
|
|||
|
|
self.log(" 2. Press <leader>a (usually comma+a)")
|
|||
|
|
self.log(" 3. Enter your instruction")
|
|||
|
|
self.log(" 4. Text is transformed instantly")
|
|||
|
|
self.log("\nDocumentation:")
|
|||
|
|
self.log(" - Quick start: QUICK_START.md")
|
|||
|
|
self.log(" - Detailed setup: SETUP.md")
|
|||
|
|
self.log(" - Test cases: TEST_CASES.md")
|
|||
|
|
self.log(" - Technical details: TECHNICAL.md")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
self.log("Installation failed", "ERROR")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def verify_installation(self):
|
|||
|
|
"""Verify that plugin files exist and are readable"""
|
|||
|
|
self.log("\nVerifying installation...")
|
|||
|
|
|
|||
|
|
all_good = True
|
|||
|
|
for editor, path in self.installed:
|
|||
|
|
if path.exists() and path.is_file():
|
|||
|
|
size = path.stat().st_size
|
|||
|
|
self.log(f"✓ {editor} plugin verified ({size} bytes)")
|
|||
|
|
else:
|
|||
|
|
self.log(f"✗ {editor} plugin not found at {path}", "ERROR")
|
|||
|
|
all_good = False
|
|||
|
|
|
|||
|
|
return all_good
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
parser = argparse.ArgumentParser(
|
|||
|
|
description="Install Claude Filter Vim plugin",
|
|||
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|||
|
|
epilog="""
|
|||
|
|
Examples:
|
|||
|
|
python3 install.py # Install with auto-detection
|
|||
|
|
python3 install.py --verbose # Show detailed output
|
|||
|
|
python3 install.py --override # Update existing installation
|
|||
|
|
python3 install.py -v -o # Both verbose and override
|
|||
|
|
"""
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
parser.add_argument(
|
|||
|
|
"-v", "--verbose",
|
|||
|
|
action="store_true",
|
|||
|
|
help="Show detailed installation messages"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
parser.add_argument(
|
|||
|
|
"-o", "--override",
|
|||
|
|
action="store_true",
|
|||
|
|
help="Override existing installation (update)"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
parser.add_argument(
|
|||
|
|
"--verify",
|
|||
|
|
action="store_true",
|
|||
|
|
help="Only verify existing installation"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
args = parser.parse_args()
|
|||
|
|
|
|||
|
|
installer = PluginInstaller(verbose=args.verbose, override=args.override)
|
|||
|
|
|
|||
|
|
if args.verify:
|
|||
|
|
installer.install()
|
|||
|
|
installer.verify_installation()
|
|||
|
|
else:
|
|||
|
|
success = installer.install()
|
|||
|
|
if success:
|
|||
|
|
installer.verify_installation()
|
|||
|
|
sys.exit(0)
|
|||
|
|
else:
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|