11from os import environ
22from os .path import commonpath
33from pathlib import PurePath , Path
4- from typing import List , Dict , Any , Union , Tuple
4+ from typing import List , Dict , Any , Union , Tuple , Optional
5+ from pygit2 import DiffHunk # type: ignore
56from .loggers import logger , start_log_group
67
78#: A path to generated cache artifacts. (only used when verbosity is in debug mode)
@@ -19,16 +20,21 @@ class FileObj:
1920 for all hunks in the diff.
2021 """
2122
22- def __init__ (self , name : str , additions : List [int ], diff_chunks : List [List [int ]]):
23+ def __init__ (
24+ self ,
25+ name : str ,
26+ additions : Optional [List [int ]] = None ,
27+ diff_chunks : Optional [List [List [int ]]] = None ,
28+ ):
2329 self .name : str = name #: The file name
24- self .additions : List [int ] = additions
30+ self .additions : List [int ] = additions or []
2531 """A list of line numbers that contain added changes. This will be empty if
2632 not focusing on lines changed only."""
27- self .diff_chunks : List [List [int ]] = diff_chunks
33+ self .diff_chunks : List [List [int ]] = diff_chunks or []
2834 """A list of line numbers that define the beginning and ending of hunks in the
2935 diff. This will be empty if not focusing on lines changed only."""
3036 self .lines_added : List [List [int ]] = FileObj ._consolidate_list_to_ranges (
31- additions
37+ additions or []
3238 )
3339 """A list of line numbers that define the beginning and ending of ranges that
3440 have added changes. This will be empty if not focusing on lines changed only.
@@ -93,6 +99,39 @@ def serialize(self) -> Dict[str, Any]:
9399 },
94100 }
95101
102+ def is_hunk_contained (self , hunk : DiffHunk ) -> Optional [Tuple [int , int ]]:
103+ """Does a given ``hunk`` start and end within a single diff hunk?
104+
105+ This also includes some compensations for hunk headers that are oddly formed.
106+
107+ .. tip:: This is mostly useful to create comments that can be posted within a
108+ git changes' diff. Ideally, designed for PR reviews based on patches
109+ generated by clang tools' output.
110+
111+ :returns: The appropriate starting and ending line numbers of the given hunk.
112+ If hunk cannot fit in a single hunk, this returns `None`.
113+ """
114+ if hunk .old_lines > 0 :
115+ start = hunk .old_start
116+ # span of old_lines is an inclusive range
117+ end = hunk .old_start + hunk .old_lines - 1
118+ else : # if number of old lines is 0
119+ # start hunk at new line number
120+ start = hunk .new_start
121+ # make it span 1 line
122+ end = start
123+ for hunk in self .diff_chunks :
124+ chunk_range = range (hunk [0 ], hunk [1 ])
125+ if start in chunk_range and end in chunk_range :
126+ return (start , end )
127+ logger .warning (
128+ "lines %d - %d are not within a single diff hunk for file %s." ,
129+ start ,
130+ end ,
131+ self .name ,
132+ )
133+ return None
134+
96135
97136def is_file_in_list (paths : List [str ], file_name : str , prompt : str ) -> bool :
98137 """Determine if a file is specified in a list of paths and/or filenames.
@@ -195,7 +234,7 @@ def list_source_files(
195234 if is_file_in_list (
196235 not_ignored , file_path , "not ignored"
197236 ) or not is_file_in_list (ignored , file_path , "ignored" ):
198- files .append (FileObj (file_path , [], [] ))
237+ files .append (FileObj (file_path ))
199238 return files
200239
201240
0 commit comments