mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
Refactor code structure and remove redundant changes
This commit is contained in:
300
codex-lens/build/lib/codexlens/storage/path_mapper.py
Normal file
300
codex-lens/build/lib/codexlens/storage/path_mapper.py
Normal file
@@ -0,0 +1,300 @@
|
||||
"""Path mapping utilities for source paths and index paths.
|
||||
|
||||
This module provides bidirectional mapping between source code directories
|
||||
and their corresponding index storage locations.
|
||||
|
||||
Storage Structure:
|
||||
~/.codexlens/
|
||||
├── registry.db # Global mapping table
|
||||
└── indexes/
|
||||
└── D/
|
||||
└── Claude_dms3/
|
||||
├── _index.db # Root directory index
|
||||
└── src/
|
||||
└── _index.db # src/ directory index
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def _get_configured_index_root() -> Path:
|
||||
"""Get the index root from environment or config file.
|
||||
|
||||
Priority order:
|
||||
1. CODEXLENS_INDEX_DIR environment variable
|
||||
2. index_dir from ~/.codexlens/config.json
|
||||
3. Default: ~/.codexlens/indexes
|
||||
"""
|
||||
env_override = os.getenv("CODEXLENS_INDEX_DIR")
|
||||
if env_override:
|
||||
return Path(env_override).expanduser().resolve()
|
||||
|
||||
config_file = Path.home() / ".codexlens" / "config.json"
|
||||
if config_file.exists():
|
||||
try:
|
||||
cfg = json.loads(config_file.read_text(encoding="utf-8"))
|
||||
if "index_dir" in cfg:
|
||||
return Path(cfg["index_dir"]).expanduser().resolve()
|
||||
except (json.JSONDecodeError, OSError):
|
||||
pass
|
||||
|
||||
return Path.home() / ".codexlens" / "indexes"
|
||||
|
||||
|
||||
class PathMapper:
|
||||
"""Bidirectional mapping tool for source paths ↔ index paths.
|
||||
|
||||
Handles cross-platform path normalization and conversion between
|
||||
source code directories and their index storage locations.
|
||||
|
||||
Attributes:
|
||||
DEFAULT_INDEX_ROOT: Default root directory for all indexes
|
||||
INDEX_DB_NAME: Standard name for index database files
|
||||
index_root: Configured index root directory
|
||||
"""
|
||||
|
||||
DEFAULT_INDEX_ROOT = _get_configured_index_root()
|
||||
INDEX_DB_NAME = "_index.db"
|
||||
|
||||
def __init__(self, index_root: Optional[Path] = None):
|
||||
"""Initialize PathMapper with optional custom index root.
|
||||
|
||||
Args:
|
||||
index_root: Custom index root directory. If None, uses DEFAULT_INDEX_ROOT.
|
||||
"""
|
||||
self.index_root = (index_root or self.DEFAULT_INDEX_ROOT).resolve()
|
||||
|
||||
def source_to_index_dir(self, source_path: Path) -> Path:
|
||||
"""Convert source directory to its index directory path.
|
||||
|
||||
Maps a source code directory to where its index data should be stored.
|
||||
The mapping preserves the directory structure but normalizes paths
|
||||
for cross-platform compatibility.
|
||||
|
||||
Args:
|
||||
source_path: Source directory path to map
|
||||
|
||||
Returns:
|
||||
Index directory path under index_root
|
||||
|
||||
Examples:
|
||||
>>> mapper = PathMapper()
|
||||
>>> mapper.source_to_index_dir(Path("D:/Claude_dms3/src"))
|
||||
PosixPath('/home/user/.codexlens/indexes/D/Claude_dms3/src')
|
||||
|
||||
>>> mapper.source_to_index_dir(Path("/home/user/project"))
|
||||
PosixPath('/home/user/.codexlens/indexes/home/user/project')
|
||||
"""
|
||||
source_path = source_path.resolve()
|
||||
normalized = self.normalize_path(source_path)
|
||||
return self.index_root / normalized
|
||||
|
||||
def source_to_index_db(self, source_path: Path) -> Path:
|
||||
"""Convert source directory to its index database file path.
|
||||
|
||||
Maps a source directory to the full path of its index database file,
|
||||
including the standard INDEX_DB_NAME.
|
||||
|
||||
Args:
|
||||
source_path: Source directory path to map
|
||||
|
||||
Returns:
|
||||
Full path to the index database file
|
||||
|
||||
Examples:
|
||||
>>> mapper = PathMapper()
|
||||
>>> mapper.source_to_index_db(Path("D:/Claude_dms3/src"))
|
||||
PosixPath('/home/user/.codexlens/indexes/D/Claude_dms3/src/_index.db')
|
||||
"""
|
||||
index_dir = self.source_to_index_dir(source_path)
|
||||
return index_dir / self.INDEX_DB_NAME
|
||||
|
||||
def index_to_source(self, index_path: Path) -> Path:
|
||||
"""Convert index path back to original source path.
|
||||
|
||||
Performs reverse mapping from an index storage location to the
|
||||
original source directory. Handles both directory paths and
|
||||
database file paths.
|
||||
|
||||
Args:
|
||||
index_path: Index directory or database file path
|
||||
|
||||
Returns:
|
||||
Original source directory path
|
||||
|
||||
Raises:
|
||||
ValueError: If index_path is not under index_root
|
||||
|
||||
Examples:
|
||||
>>> mapper = PathMapper()
|
||||
>>> mapper.index_to_source(
|
||||
... Path("~/.codexlens/indexes/D/Claude_dms3/src/_index.db")
|
||||
... )
|
||||
WindowsPath('D:/Claude_dms3/src')
|
||||
|
||||
>>> mapper.index_to_source(
|
||||
... Path("~/.codexlens/indexes/D/Claude_dms3/src")
|
||||
... )
|
||||
WindowsPath('D:/Claude_dms3/src')
|
||||
"""
|
||||
index_path = index_path.resolve()
|
||||
|
||||
# Remove _index.db if present
|
||||
if index_path.name == self.INDEX_DB_NAME:
|
||||
index_path = index_path.parent
|
||||
|
||||
# Verify path is under index_root
|
||||
try:
|
||||
relative = index_path.relative_to(self.index_root)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Index path {index_path} is not under index root {self.index_root}"
|
||||
)
|
||||
|
||||
# Convert normalized path back to source path
|
||||
normalized_str = str(relative).replace("\\", "/")
|
||||
return self.denormalize_path(normalized_str)
|
||||
|
||||
def get_project_root(self, source_path: Path) -> Path:
|
||||
"""Find the project root directory (topmost indexed directory).
|
||||
|
||||
Walks up the directory tree to find the highest-level directory
|
||||
that has an index database.
|
||||
|
||||
Args:
|
||||
source_path: Source directory to start from
|
||||
|
||||
Returns:
|
||||
Project root directory path. Returns source_path itself if
|
||||
no parent index is found.
|
||||
|
||||
Examples:
|
||||
>>> mapper = PathMapper()
|
||||
>>> mapper.get_project_root(Path("D:/Claude_dms3/src/codexlens"))
|
||||
WindowsPath('D:/Claude_dms3')
|
||||
"""
|
||||
source_path = source_path.resolve()
|
||||
current = source_path
|
||||
project_root = source_path
|
||||
|
||||
# Walk up the tree
|
||||
while current.parent != current: # Stop at filesystem root
|
||||
parent_index_db = self.source_to_index_db(current.parent)
|
||||
if parent_index_db.exists():
|
||||
project_root = current.parent
|
||||
current = current.parent
|
||||
else:
|
||||
break
|
||||
|
||||
return project_root
|
||||
|
||||
def get_relative_depth(self, source_path: Path, project_root: Path) -> int:
|
||||
"""Calculate directory depth relative to project root.
|
||||
|
||||
Args:
|
||||
source_path: Target directory path
|
||||
project_root: Project root directory path
|
||||
|
||||
Returns:
|
||||
Number of directory levels from project_root to source_path
|
||||
|
||||
Raises:
|
||||
ValueError: If source_path is not under project_root
|
||||
|
||||
Examples:
|
||||
>>> mapper = PathMapper()
|
||||
>>> mapper.get_relative_depth(
|
||||
... Path("D:/Claude_dms3/src/codexlens"),
|
||||
... Path("D:/Claude_dms3")
|
||||
... )
|
||||
2
|
||||
"""
|
||||
source_path = source_path.resolve()
|
||||
project_root = project_root.resolve()
|
||||
|
||||
try:
|
||||
relative = source_path.relative_to(project_root)
|
||||
# Count path components
|
||||
return len(relative.parts)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Source path {source_path} is not under project root {project_root}"
|
||||
)
|
||||
|
||||
def normalize_path(self, path: Path) -> str:
|
||||
"""Normalize path to cross-platform storage format.
|
||||
|
||||
Converts OS-specific paths to a standardized format for storage:
|
||||
- Windows: Removes drive colons (D: → D)
|
||||
- Unix: Removes leading slash
|
||||
- Uses forward slashes throughout
|
||||
|
||||
Args:
|
||||
path: Path to normalize
|
||||
|
||||
Returns:
|
||||
Normalized path string
|
||||
|
||||
Examples:
|
||||
>>> mapper = PathMapper()
|
||||
>>> mapper.normalize_path(Path("D:/path/to/dir"))
|
||||
'D/path/to/dir'
|
||||
|
||||
>>> mapper.normalize_path(Path("/home/user/path"))
|
||||
'home/user/path'
|
||||
"""
|
||||
path = path.resolve()
|
||||
path_str = str(path)
|
||||
|
||||
# Handle Windows paths with drive letters
|
||||
if platform.system() == "Windows" and len(path.parts) > 0:
|
||||
# Convert D:\path\to\dir → D/path/to/dir
|
||||
drive = path.parts[0].replace(":", "") # D: → D
|
||||
rest = Path(*path.parts[1:]) if len(path.parts) > 1 else Path()
|
||||
normalized = f"{drive}/{rest}".replace("\\", "/")
|
||||
return normalized.rstrip("/")
|
||||
|
||||
# Handle Unix paths
|
||||
# /home/user/path → home/user/path
|
||||
return path_str.lstrip("/").replace("\\", "/")
|
||||
|
||||
def denormalize_path(self, normalized: str) -> Path:
|
||||
"""Convert normalized path back to OS-specific path.
|
||||
|
||||
Reverses the normalization process to restore OS-native path format:
|
||||
- Windows: Adds drive colons (D → D:)
|
||||
- Unix: Adds leading slash
|
||||
|
||||
Args:
|
||||
normalized: Normalized path string
|
||||
|
||||
Returns:
|
||||
OS-specific Path object
|
||||
|
||||
Examples:
|
||||
>>> mapper = PathMapper()
|
||||
>>> mapper.denormalize_path("D/path/to/dir") # On Windows
|
||||
WindowsPath('D:/path/to/dir')
|
||||
|
||||
>>> mapper.denormalize_path("home/user/path") # On Unix
|
||||
PosixPath('/home/user/path')
|
||||
"""
|
||||
parts = normalized.split("/")
|
||||
|
||||
# Handle Windows paths
|
||||
if platform.system() == "Windows" and len(parts) > 0:
|
||||
# Check if first part is a drive letter
|
||||
if len(parts[0]) == 1 and parts[0].isalpha():
|
||||
# D/path/to/dir → D:/path/to/dir
|
||||
drive = f"{parts[0]}:"
|
||||
if len(parts) > 1:
|
||||
return Path(drive) / Path(*parts[1:])
|
||||
return Path(drive)
|
||||
|
||||
# Handle Unix paths or relative paths
|
||||
# home/user/path → /home/user/path
|
||||
return Path("/") / Path(*parts)
|
||||
Reference in New Issue
Block a user