נרמול קוד (Code Normalization)
מסמך זה מרכז את כל מה שסוכן או מפתח צריך לדעת על מנגנון נרמול הקוד של Code Keeper Bot – למה הוא קיים, איך הוא עובד ואיך משתמשים בו ביום־יום.
למה בכלל מנרמלים?
אחידות בנתונים – ניהול Snippets ממקורות שונים (טלגרם, WebApp, ייבוא קבצים) בלי הפתעות של CRLF, BOM או תווים נסתרים.
מניעת באגים ברינדור – הורדת תווים בלתי נראים מונעת הדבקות ב-HTML/Markdown ותאונות בתצוגה.
שמירה על היסטוריה נקייה – קובץ שלא משתנה רק כי נערך מ-Windows/VS Code לא מייצר diff מיותר.
נוחות טסטים – טסט דטרמיניסטי שמקבל פלט צפוי, במיוחד כשמריצים Async Handlers או שירותי אפליקציה.
מתי ואיפה המנגנון רץ?
handlers/save_flow.py– כל קלט של המשתמש עובר דרךutils.normalize_code(...).SnippetService.create_snippet– קורא ישירות ל-CodeNormalizerהדומייני ומבטיח שהנתונים שנשמרים ל-DB אחידים.services/code_service.pyוזרימות גיבוי/שחזור – כולם נשענים על אותו API כדי למנוע פיצולים.WebApp (
webapp/app.py) – לפני שמירת טפסים במצב מתקדם או שיתוף מהעורך.ספריות חיצוניות שמחזיקות קוד (כמו import דרך CLI) מחויבות להפעיל את ה-API הזה לפני כתיבה לדאטה-בייס.
אם לא ניתן לייבא את שכבת הדומיין (למשל בשל Bootstrapping מוקדם), הקריאה ל-utils.normalize_code תריץ fallback פנימי שמסונכרן מול CodeNormalizer.
מה בדיוק קורה במהלך הנרמול?
המרת escape literals – רצפים כמו
\u200Bאו\U0001F600שמייצגים תווי פורמט נסתרים נמחקים.הסרת BOM – מונע אזכורים כפולים בתחילת קובץ.
איחוד שורות –
\r\nאו\rמוחלפים ב-\n.החלפת רווחים מיוחדים – NBSP/NNBSP ותווי Zs אחרים הופכים לרווח רגיל.
סילוק תווי רוחב-אפס, סימוני כיוון ותווי בקרה/פורמט – פרט ל-
\t,\n,\rשנשמרים.Trim per line – רווחים וטאבים בסוף כל שורה נמחקים.
אין שורה ריקה מאולצת – לאחר ה-Trim, מסירים גם
\nעודפים בסוף הטקסט. אם צריך newline – דואגים אליו לפני הקריאה לנרמול.
⚠️ אין שינוי במבנה התוכן עצמו (לא מיישרים אינדנטציה, לא מריצים formatter). זהו מנגנון הגיינה, לא opinionated formatter.
איך לבחור API?
מצב |
מה להשתמש |
הערות |
|---|---|---|
קוד בתוך שכבת דומיין / שירותי אפליקציה |
|
מומלץ להחזיק singleton (כמו ב- |
Handler / Utility שלא יכול לייבא את הדומיין |
|
קורא ל-CodeNormalizer מאחורי הקלעים כשאפשר, או מריץ fallback זהה. |
טסטים |
ישירות |
נותן תוצאה צפויה גם בלי bootstrapping של utils. |
דגשים לסוכנים בעת כתיבת קוד
שמרו על אחריות בשכבות – Handlers לא אמורים לדעת על פרטי הנרמול. שירותי אפליקציה או domain helpers צריכים לטפל בזה.
אל תוסיפו
strip()אגרסיבי לפני הקריאה – להבדיל מ-Trim של סוף שורה, אנחנו שומרים על כל הרווחים בראש הטקסט ובאמצעו.תמיד התייחסו ל-EOF – אם אתם צריכים newline בסוף (למשל בעת יצוא ל-patch), חברו
code + "\n"לפני הנרמול או אחרי השמירה.שמרו על בדיקות – כותבים טסט? תעדיף assert על פלט
CodeNormalizerכדי לא לכפול לוגיקה.קלט לא-מחרוזתי – גם utils וגם ה-CodeNormalizer מחזירים את הערך המקורי (או
""עבורNone). וודאו שאתם יודעים להתמודד עם זה.
טסטים, דיבוג ושחזור תקלות
Unit Tests – ר«
tests/unit/domain/services/test_code_normalizer.pyכדוגמה. כל edge case מקבלassertאחד ברור.שילוב בשירותים –
tests/unit/application/services/test_snippet_service.pyמוודא שהשירות משתמש בנרמול לפני שמירת Snippet.בדיקות רגרסיה – כשנוגעים בתסריטי Save Flow, עדכנו גם את התיעוד וגם את הטסטים, וודאו שהמחרוזת הצפויה לא כוללת newline עודף.
דיבאג מהיר – בחמ“ל אפשר להריץ
python3 - <<'PY'ולהדפיסrepr(CodeNormalizer().normalize(...))כדי להבין מה המשתמש קיבל.
שאלות נפוצות
למה לא להוסיף newline בסוף? – כי המשתמש/ה-handler צריכים שליטה מלאה. יש תסריטים (למשל פקודות חד-שורתיות) שבהם newline ייצור תוצאות שגויות.
מה לגבי קבצים בינאריים? – המערכת לא מעבדת אותם עם הנורמלייזר; לפני שמירת קובץ בינארי חובה לעבוד עם storage ייעודי.
צריך לנקות גם בתוך קבצי DB קיימים? – לא. תהליך המיגרציה יטפל בזה; סוכנים נוגעים רק בנתונים חדשים.
אפשר להרחיב את המנגנון (למשל טאב ל-4 רווחים)? – לא במסגרת הנרמול. שינויים כאלו יישקלו כחלק מפורמט חדש (formatter) וידרשו RFC נפרד.
מה לעשות כשמוסיפים זרימה חדשה?
קבעו נקודת נרמול אחת (Domain Service / Utility).
תעדו בקצרה ב-PR שהזרימה משתמשת בנרמול (קישור לדף זה).
הוסיפו טסט שמוודא שהפלט נקי מרווחי סוף ו-CRLF.
אם תסריט תלוי בשורת סיום – תעדו זאת בזרימת העבודה (ראה גם
docs/workflows/save-flow.rst).
קישורים רלוונטיים
:doc:
../workflows/save-flow:doc:
../architecture:doc:
../whats-newקוד המקור:
src/domain/services/code_normalizer.py,utils.py