diff --git a/syntax_helper/gui.py b/syntax_helper/gui.py index 7237c1c..c43b861 100644 --- a/syntax_helper/gui.py +++ b/syntax_helper/gui.py @@ -9,6 +9,9 @@ from .service import SyntaxAnalyzerService +RESULT_SEPARATOR = "─" * 20 + + class LineNumberedEditor(ttk.Frame): def __init__(self, master: tk.Misc) -> None: super().__init__(master) @@ -98,8 +101,8 @@ class SyntaxErrorHelperApp(TkinterDnD.Tk): def __init__(self) -> None: super().__init__() self.title("構文エラー解析ツール") - self.geometry("1000x720") - self.minsize(720, 520) + self.geometry("1200x720") + self.minsize(900, 520) self.service = SyntaxAnalyzerService() self._build_ui() @@ -117,7 +120,7 @@ def _build_ui(self) -> None: self.status = tk.StringVar(value="対応ファイルをエディタへドラッグ&ドロップして読み込めます。") ttk.Label(self, textvariable=self.status, foreground="#555555", padding=(10, 0, 10, 6)).pack(fill="x") - pane = ttk.Panedwindow(self, orient="vertical") + pane = ttk.Panedwindow(self, orient="horizontal") pane.pack(fill="both", expand=True, padx=10, pady=(0, 10)) editor_frame = ttk.LabelFrame(pane, text="ソースコード", padding=4) self.editor = LineNumberedEditor(editor_frame) @@ -128,8 +131,26 @@ def _build_ui(self) -> None: pane.add(editor_frame, weight=3) result_frame = ttk.LabelFrame(pane, text="解析結果・修正候補", padding=4) - self.result = tk.Text(result_frame, wrap="word", height=12, state="disabled", + self.result = tk.Text(result_frame, wrap="word", width=42, state="disabled", background="#fafafa", font=("Yu Gothic UI", 10)) + self.result.tag_configure( + "analysis", + background="#ffe8e8", + lmargin1=6, + lmargin2=6, + rmargin=6, + spacing1=4, + spacing3=4, + ) + self.result.tag_configure( + "suggestion", + background="#e8f5e9", + lmargin1=6, + lmargin2=6, + rmargin=6, + spacing1=4, + spacing3=4, + ) result_scroll = ttk.Scrollbar(result_frame, orient="vertical", command=self.result.yview) self.result.configure(yscrollcommand=result_scroll.set) self.result.pack(side="left", fill="both", expand=True) @@ -162,15 +183,17 @@ def _clear_results(self) -> None: def _analyze(self) -> None: results = self.service.analyze(self.language.get(), self.editor.get()) self.editor.highlight(results) - if results: - content = "\n\n" + ("\n\n" + "─" * 40 + "\n\n").join( - result.to_japanese_text() for result in results - ) - else: - content = "構文エラー候補は見つかりませんでした。" self.result.configure(state="normal") self.result.delete("1.0", "end") - self.result.insert("1.0", content.strip()) + if not results: + self.result.insert("1.0", "構文エラー候補は見つかりませんでした。") + else: + for index, result in enumerate(results): + if index: + self.result.insert("end", f"\n{RESULT_SEPARATOR}\n\n") + self.result.insert("end", result.analysis_text() + "\n", "analysis") + self.result.insert("end", "\n") + self.result.insert("end", result.suggestion_text() + "\n", "suggestion") self.result.configure(state="disabled") diff --git a/syntax_helper/model.py b/syntax_helper/model.py index 8fd3b79..37758a2 100644 --- a/syntax_helper/model.py +++ b/syntax_helper/model.py @@ -17,11 +17,17 @@ class AnalysisResult: reason: str suggestion: str - def to_japanese_text(self) -> str: + def location_text(self) -> str: location = f"{self.error_line}行目付近に問題がある可能性があります。" if self.suspected_line != self.error_line: location += f"\n原因候補は{self.suspected_line}行目です。" - return ( - f"{location}\n\n{self.message}\n\n" - f"理由: {self.reason}\n\n修正候補:\n{self.suggestion}" - ) + return location + + def analysis_text(self) -> str: + return f"{self.location_text()}\n\n{self.message}\n\n理由: {self.reason}" + + def suggestion_text(self) -> str: + return f"修正候補:\n{self.suggestion}" + + def to_japanese_text(self) -> str: + return f"{self.analysis_text()}\n\n{self.suggestion_text()}" diff --git a/tests/test_model.py b/tests/test_model.py new file mode 100644 index 0000000..0be34aa --- /dev/null +++ b/tests/test_model.py @@ -0,0 +1,32 @@ +import unittest + +from syntax_helper.model import AnalysisResult, Severity + + +class AnalysisResultTest(unittest.TestCase): + def setUp(self) -> None: + self.result = AnalysisResult( + severity=Severity.ERROR, + language="C", + error_line=2, + suspected_line=1, + message="セミコロンが不足している可能性があります。", + reason="次の行でエラーになることがあります。", + suggestion="int value = 10;", + ) + + def test_analysis_and_suggestion_are_separate_sections(self) -> None: + self.assertIn("原因候補は1行目です。", self.result.analysis_text()) + self.assertIn("理由:", self.result.analysis_text()) + self.assertNotIn("修正候補:", self.result.analysis_text()) + self.assertEqual("修正候補:\nint value = 10;", self.result.suggestion_text()) + + def test_combined_text_keeps_existing_format(self) -> None: + self.assertEqual( + f"{self.result.analysis_text()}\n\n{self.result.suggestion_text()}", + self.result.to_japanese_text(), + ) + + +if __name__ == "__main__": + unittest.main()