1+ import re
12from itertools import chain
23from typing import TYPE_CHECKING , Dict , Iterable , Iterator , List , NamedTuple , Optional , Sequence , Tuple
34
@@ -51,10 +52,11 @@ def __init__(self, namespace: "Namespace", library_doc: LibraryDoc) -> None:
5152 Optional [List [KeywordDoc ]],
5253 ],
5354 ] = {}
54- self . handle_bdd_style = True
55+
5556 self ._all_keywords : Optional [List [LibraryEntry ]] = None
5657 self ._resource_keywords : Optional [List [ResourceEntry ]] = None
5758 self ._library_keywords : Optional [List [LibraryEntry ]] = None
59+ self ._bdd_prefix_regexp : Optional ["re.Pattern[str]" ] = None
5860
5961 def reset_diagnostics (self ) -> None :
6062 self .diagnostics = []
@@ -70,17 +72,15 @@ def find_keyword(
7072 try :
7173 self .reset_diagnostics ()
7274
73- self .handle_bdd_style = handle_bdd_style
74-
75- cached = self ._cache .get ((name , self .handle_bdd_style ), None )
75+ cached = self ._cache .get ((name , handle_bdd_style ), None )
7676
7777 if cached is not None :
7878 self .diagnostics = cached [1 ]
7979 self .multiple_keywords_result = cached [2 ]
8080 return cached [0 ]
8181
8282 try :
83- result = self ._find_keyword (name )
83+ result = self ._find_keyword (name , handle_bdd_style )
8484 if result is None :
8585 self .diagnostics .append (
8686 DiagnosticsEntry (
@@ -99,7 +99,7 @@ def find_keyword(
9999 result = None
100100 self .diagnostics .append (DiagnosticsEntry (str (e ), DiagnosticSeverity .ERROR , Error .KEYWORD_ERROR ))
101101
102- self ._cache [(name , self . handle_bdd_style )] = (
102+ self ._cache [(name , handle_bdd_style )] = (
103103 result ,
104104 self .diagnostics ,
105105 self .multiple_keywords_result ,
@@ -109,7 +109,11 @@ def find_keyword(
109109 except CancelSearchError :
110110 return None
111111
112- def _find_keyword (self , name : Optional [str ]) -> Optional [KeywordDoc ]:
112+ def _find_keyword (
113+ self ,
114+ name : Optional [str ],
115+ handle_bdd_style : bool = True ,
116+ ) -> Optional [KeywordDoc ]:
113117 if not name :
114118 self .diagnostics .append (
115119 DiagnosticsEntry (
@@ -129,14 +133,21 @@ def _find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]:
129133 )
130134 raise CancelSearchError
131135
132- result = self ._get_keyword_from_self (name )
136+ result : Optional [KeywordDoc ] = None
137+
138+ if get_robot_version () >= (7 , 0 ) and handle_bdd_style :
139+ result = self ._get_bdd_style_keyword (name )
140+
141+ if not result :
142+ result = self ._get_keyword_from_self (name )
143+
133144 if not result and "." in name :
134145 result = self ._get_explicit_keyword (name )
135146
136147 if not result :
137148 result = self ._get_implicit_keyword (name )
138149
139- if not result and self . handle_bdd_style :
150+ if get_robot_version () < ( 7 , 0 ) and not result and handle_bdd_style :
140151 return self ._get_bdd_style_keyword (name )
141152
142153 return result
@@ -264,6 +275,9 @@ def _prioritize_same_file_or_public(
264275 def _select_best_matches (
265276 self , entries : List [Tuple [Optional [LibraryEntry ], KeywordDoc ]]
266277 ) -> List [Tuple [Optional [LibraryEntry ], KeywordDoc ]]:
278+ if len (entries ) < 2 :
279+ return entries
280+
267281 normal = [hand for hand in entries if not hand [1 ].is_embedded ]
268282 if normal :
269283 return normal
@@ -438,21 +452,25 @@ def _create_custom_and_standard_keyword_conflict_warning_message(
438452 f"or '{ '' if standard [0 ] is None else standard [0 ].alias or standard [0 ].name } .{ standard [1 ].name } '."
439453 )
440454
441- def _get_bdd_style_keyword (self , name : str ) -> Optional [KeywordDoc ]:
442- if get_robot_version () < (6 , 0 ):
443- lower = name .lower ()
444- for prefix in ["given " , "when " , "then " , "and " , "but " ]:
445- if lower .startswith (prefix ):
446- return self ._find_keyword (name [len (prefix ) :])
447- return None
455+ @property
456+ def bdd_prefix_regexp (self ) -> "re.Pattern[str]" :
457+ if not self ._bdd_prefix_regexp :
458+ prefixes = (
459+ "|" .join (
460+ self .namespace .languages .bdd_prefixes
461+ if self .namespace .languages is not None
462+ else ["given" , "when" , "then" , "and" , "but" ]
463+ )
464+ .replace (" " , r"\s" )
465+ .lower ()
466+ )
467+ self ._bdd_prefix_regexp = re .compile (rf"({ prefixes } )\s" , re .IGNORECASE )
468+ return self ._bdd_prefix_regexp
448469
449- parts = name .split ()
450- if len (parts ) < 2 :
451- return None
452- for index in range (1 , len (parts )):
453- prefix = " " .join (parts [:index ]).title ()
454- if prefix .title () in (
455- self .namespace .languages .bdd_prefixes if self .namespace .languages is not None else DEFAULT_BDD_PREFIXES
456- ):
457- return self ._find_keyword (" " .join (parts [index :]))
470+ def _get_bdd_style_keyword (self , name : str ) -> Optional [KeywordDoc ]:
471+ match = self .bdd_prefix_regexp .match (name )
472+ if match :
473+ return self ._find_keyword (
474+ name [match .end () :], handle_bdd_style = False if get_robot_version () >= (7 , 0 ) else True
475+ )
458476 return None
0 commit comments