88
99from __future__ import annotations
1010
11+ import re
1112from pathlib import Path
1213
1314from ..protocol import Hit
1415
1516_SNIPPET_RADIUS = 120
1617
18+ # Mindestlaenge fuer Suchterme; kuerzere Tokens sind fast immer Rauschen.
19+ _MIN_TERM_LENGTH = 3
20+
21+ # Wortzeichen (inkl. Umlaute) und Bindestrich -- Satzzeichen fallen so
22+ # beim Tokenisieren automatisch weg (".TOPICS," -> "TOPICS").
23+ _TOKEN_PATTERN = re .compile (r"[\w-]+" , re .UNICODE )
24+
25+ # Stopwoerter Deutsch + Englisch: Fuellwoerter ohne Eigeninfo. Als Suchterm
26+ # treffen sie per Substr-Match fast jeden Text ("fuer" in "Verfuegung")
27+ # und dominieren das Ranking. Wartbar als Modul-Konstante.
28+ _STOPWORDS = frozenset (
29+ {
30+ # Deutsch
31+ "aber" , "alle" , "als" , "am" , "an" , "auch" , "auf" , "aus" , "bei" ,
32+ "beim" , "bin" , "bis" , "da" , "dann" , "das" , "dass" , "dem" , "den" ,
33+ "der" , "des" , "die" , "dies" , "diese" , "dieser" , "du" , "durch" ,
34+ "ein" , "eine" , "einem" , "einen" , "einer" , "er" , "es" , "für" ,
35+ "fuer" , "habe" , "haben" , "hat" , "ich" , "ihm" , "ihn" , "ihr" ,
36+ "ihre" , "im" , "in" , "ins" , "ist" , "kein" , "keine" , "man" , "mein" ,
37+ "meine" , "mit" , "nach" , "nicht" , "noch" , "nur" , "ob" , "oder" ,
38+ "ohne" , "sein" , "seine" , "sich" , "sie" , "sind" , "so" , "über" ,
39+ "ueber" , "um" , "und" , "uns" , "vom" , "von" , "vor" , "war" , "was" ,
40+ "weil" , "wenn" , "wie" , "wir" , "wird" , "wo" , "zu" , "zum" , "zur" ,
41+ # Englisch
42+ "the" , "and" , "for" , "with" , "this" , "that" , "from" , "have" ,
43+ "has" , "are" , "was" , "were" , "not" , "but" , "all" , "can" , "you" ,
44+ "your" , "its" , "our" , "their" , "them" , "they" , "what" , "which" ,
45+ "when" , "where" , "how" , "then" , "there" , "here" , "into" , "only" ,
46+ "very" , "just" ,
47+ }
48+ )
49+
1750
1851class FilesBackend :
1952 """Sucht in ``*.md``-Dateien unterhalb eines oder mehrerer Wurzeln."""
@@ -38,7 +71,7 @@ def search(self, query: str, limit: int = 5) -> list[Hit]:
3871 if not self .available ():
3972 return []
4073
41- terms = [ t . lower () for t in query . split () if t . strip ()]
74+ terms = _terms_from_query ( query )
4275 if not terms :
4376 return []
4477
@@ -81,6 +114,20 @@ def search(self, query: str, limit: int = 5) -> list[Hit]:
81114 return hits [:limit ]
82115
83116
117+ def _terms_from_query (query : str ) -> list [str ]:
118+ """Normalisiert eine Query zu Suchtermen.
119+
120+ Kleinschreibung, Satzzeichen gestrippt, Mindestlaenge 3, Stopwoerter
121+ raus. Bleibt kein Term uebrig (leere oder reine Fuellwort-Query),
122+ liefert ``search()`` keine Treffer statt Vollrauschen.
123+ """
124+ return [
125+ term
126+ for term in _TOKEN_PATTERN .findall (query .lower ())
127+ if len (term ) >= _MIN_TERM_LENGTH and term not in _STOPWORDS
128+ ]
129+
130+
84131def _snippet (text : str , lowered : str , terms : list [str ]) -> str :
85132 for term in terms :
86133 idx = lowered .find (term )
0 commit comments