refactor(external_editor): merge TextEditorConfig + ExternalEditorConfig from models.py
Per the 4-criteria decision rule: editor configs fail C1 (only used by
the editor subsystem), fail C2 (no state machine), fail C3 (no
dedicated test file), borderline C4. MERGE into the existing
src/external_editor.py which already has ExternalEditorLauncher +
the helper functions.
This commit:
1. Adds TextEditorConfig + ExternalEditorConfig + EMPTY_TEXT_EDITOR_CONFIG
class definitions to src/external_editor.py at the top.
2. Removes the same class defs from src/models.py.
3. Adds lazy re-export via the existing __getattr__ in src/models.py
(EAGER would cycle: external_editor was previously importing from
models; if models re-exports, the cycle would deadlock on initial
load).
4. Updates external_editor.py imports to no longer import from models
(the class defs are now local).
Verification: VC8 (TextEditorConfig + ExternalEditorConfig)
from src.external_editor import TextEditorConfig, ExternalEditorConfig,
EMPTY_TEXT_EDITOR_CONFIG # OK
from src.models import TextEditorConfig, ExternalEditorConfig,
EMPTY_TEXT_EDITOR_CONFIG # OK (lazy)
identity check: True for all 3
Tests verified (22/22 PASS):
tests/test_external_editor.py (17 tests)
tests/test_external_editor_gui.py (5 tests)
This commit is contained in:
+57
-4
@@ -6,11 +6,64 @@ import subprocess
|
||||
import tempfile
|
||||
|
||||
# TODO(Ed): Eliminate these?
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Dict, Any
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
from src.models import ExternalEditorConfig, TextEditorConfig
|
||||
from src.result_types import ErrorInfo, ErrorKind, Result
|
||||
from src.result_types import ErrorInfo, ErrorKind, Result
|
||||
from src.type_aliases import Metadata
|
||||
|
||||
|
||||
@dataclass
|
||||
class TextEditorConfig:
|
||||
name: str = ""
|
||||
path: str = ""
|
||||
diff_args: List[str] = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> Metadata:
|
||||
return {
|
||||
"name": self.name,
|
||||
"path": self.path,
|
||||
"diff_args": self.diff_args,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Metadata) -> "TextEditorConfig":
|
||||
return cls(
|
||||
name = data["name"],
|
||||
path = data["path"],
|
||||
diff_args = data.get("diff_args", []),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExternalEditorConfig:
|
||||
editors: Dict[str, TextEditorConfig] = field(default_factory=dict)
|
||||
default_editor: Optional[str] = None
|
||||
|
||||
def get_default(self) -> TextEditorConfig:
|
||||
if self.default_editor and self.default_editor in self.editors:
|
||||
return self.editors[self.default_editor]
|
||||
if self.editors:
|
||||
return next(iter(self.editors.values()))
|
||||
return EMPTY_TEXT_EDITOR_CONFIG
|
||||
|
||||
def to_dict(self) -> Metadata:
|
||||
return {
|
||||
"editors": {k: v.to_dict() for k, v in self.editors.items()},
|
||||
"default_editor": self.default_editor,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Metadata) -> "ExternalEditorConfig":
|
||||
editors = {}
|
||||
for name, ed_data in data.get("editors", {}).items():
|
||||
if isinstance(ed_data, dict): editors[name] = TextEditorConfig.from_dict(ed_data)
|
||||
elif isinstance(ed_data, str): editors[name] = TextEditorConfig(name=name, path=ed_data)
|
||||
return cls(editors=editors, default_editor=data.get("default_editor"))
|
||||
|
||||
|
||||
EMPTY_TEXT_EDITOR_CONFIG: TextEditorConfig = TextEditorConfig()
|
||||
|
||||
|
||||
class ExternalEditorLauncher:
|
||||
|
||||
Reference in New Issue
Block a user