1"""Anchor resolver for file:line references — ENH-1300.

2

3 Resolves a file:line reference to its enclosing function, class, or section

4 using a language-agnostic backwards regex scan (no AST). Covers Python,

5 TypeScript, JavaScript, Go, Rust, Ruby, Java, C#, and Markdown.

6"""

7

8 from __future__ import annotations

9

10 import re

11 from pathlib import Path

12

13# Each entry: (compiled pattern, kind)

14# kind is "function", "class", or "section"

15_ANCHOR_PATTERNS: list[tuple[re.Pattern[str], str]] = [

16 # Python / Ruby — def and async def

17 (re.compile(r"^[ \t]*(?:async\s+)?def\s+(\w+)\s*\("), "function"),

18 # JS / TS — function declaration or named function expression

19 (

20 re.compile(r"^[ \t]*(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s+(\w+)\s*\("),

21 "function",

22 ),

23 # JS / TS — const/let/var arrow or assigned function

24 (

25 re.compile(

26 r"^[ \t]*(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\(|function\b)"

27 ),

28 "function",

29 ),

30 # Go — top-level func and methods (optional receiver before name)

31 (re.compile(r"^func\s+(?:\([^)]+\)\s+)?(\w+)\s*[(\[]"), "function"),

32 # Rust — fn (optionally pub, async, unsafe)

33 (

34 re.compile(r"^[ \t]*(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?(?:unsafe\s+)?fn\s+(\w+)\s*[<(]"),

35 "function",

36 ),

37 # Java / C# heuristic — access-modifier(s) + return-type + name(

38 (

39 re.compile(

40 r"^[ \t]*(?:(?:public|private|protected|static|final|override|virtual|abstract|async|synchronized)\s+)"

41 r"{1,4}\w[\w<>\[\]?*]*\s+(\w+)\s*\("

42 ),

43 "function",

44 ),

45 # Universal — class / struct / interface / trait / impl / enum

46 (

47 re.compile(

48 r"^[ \t]*"

49 r"(?:(?:pub(?:\([^)]*\))?\s+|(?:public|private|protected|abstract|final|sealed|static|export|default)\s+))*"

50 r"(?:class|struct|interface|trait|impl|enum)\s+(\w+)"

51 ),

52 "class",

53 ),

54 # Markdown heading (any level, strip trailing hashes)

55 (re.compile(r"^#{1,6}\s+(.+?)(?:\s+#+)?$"), "section"),

56]

57

58

59 def resolve_anchor(file_path: str, line_number: int) -> str | None:

60 """Return the enclosing function, class, or section for the given file:line.

61

62 Scans backwards from line_number to find the nearest enclosing definition

63 using language-agnostic regexes. Works for .py, .ts, .js, .go, .rs, .rb,

64 .java, .cs, .md and any language with recognizable definition syntax.

65

66 Args:

67 file_path: Path to the source file (relative or absolute).

68 line_number: 1-based line number within the file.

69

70 Returns:

71 A human-readable anchor string such as:

72 "near function foo"

73 "near class Bar"

74 'under section "Title"'

75 or None if the file cannot be read or no anchor can be resolved.

76 """

77 try:

78 lines = Path(file_path).read_text(encoding="utf-8", errors="replace").splitlines()

79 except OSError:

80 return None

81

82 scan_end = min(line_number, len(lines))

83 for i in range(scan_end - 1, -1, -1):

84 for pattern, kind in _ANCHOR_PATTERNS:

85 m = pattern.match(lines[i])

86 if m:

87 name = m.group(1).strip()

88 if kind == "section":

89 return f'under section "{name}"'

90 return f"near {kind} {name}"

91 return None