Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 34 additions & 11 deletions syntax_helper/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()

Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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")


Expand Down
16 changes: 11 additions & 5 deletions syntax_helper/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()}"
32 changes: 32 additions & 0 deletions tests/test_model.py
Original file line number Diff line number Diff line change
@@ -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()
Loading