gitar

CommitInfo.py

1
# For now this module only serves one purpose: To make a well structured, nicely
2
# formatted file in a commit. 
3
4
# The function returns a list 
5
6
# If one line is removed, and one line is added, and both have the same line
7
# number, then that should be regarded as a changed line, not a removed+added line.
8
# This holds for each non-broken list of removed+added lines.
9
10
from pydriller.domain.commit import ModificationType
11
from ..syntax import *
12
13
""" It's a lot of annoying glue code to transform a modified file object into
14
what we actually want: The data that needs to be passed to the Django template.
15
This convenience function accepts the modified file object as given by
16
Pydriller's Commit class, and returns a list of dicts, each dict representing
17
the information needed to properly present a file diff in the template.
18
"""
19
def prepare_and_process(modified_file):
20
    #print(modified_file.source_code)
21
    result = dict()
22
    result["object"] = modified_file  # Could come in handy
23
    result["change_type"] = modified_file.change_type
24
    result["html_code_after"] = code_to_HTML(
25
        modified_file.source_code,
26
        modified_file.filename)
27
    result["html_code_before"] = code_to_HTML(
28
        modified_file.source_code_before,
29
        modified_file.filename)
30
31
    if modified_file.change_type == ModificationType.ADD:
32
        result["path"] = modified_file.new_path
33
    elif modified_file.change_type == ModificationType.DELETE:
34
        result["path"] = modified_file.old_path
35
    elif modified_file.change_type == ModificationType.RENAME:
36
        result["path"] = modified_file.old_path +" → "+ modified_file.new_path
37
    elif modified_file.change_type == ModificationType.MODIFY:
38
        result["path"] = modified_file.old_path
39
40
    added = dict()
41
    deleted = dict()
42
    for number, line in modified_file.diff_parsed["added"]:
43
        added[number] = line
44
    for number, line in modified_file.diff_parsed["deleted"]:
45
        if result["path"] == "Challenge 4/Controller.java":
46
            #print("PING")
47
            pass
48
        deleted[number] = line
49
    if result["path"] == "Challenge 4/Controller.java":
50
        pass
51
        #print("NU CONTROLLER.jAVA")
52
        #print(modified_file.diff_parsed)
53
        #print(deleted)
54
    result["rows"] = process_file(
55
        result["html_code_before"],
56
        result["html_code_after"],
57
        added,
58
        deleted)
59
60
    # These three lists are going to be empty if the methods couldn't be detected
61
    deleted, transferred, new = process_methods(modified_file)
62
    if len(deleted) == len(transferred) == len(new) == 0:
63
        result["methods_available"] = False
64
    else:
65
        result["methods_available"] = True
66
        result["deleted_methods"] = deleted
67
        result["transferred_methods"] = transferred
68
        result["new_methods"] = new
69
    return result 
70
71
""" Sometimes, the methods in a file can be detected, which adds some nice
72
information """
73
def process_methods(modified_file):
74
    before_methods = modified_file.methods_before
75
    after_methods = modified_file.methods
76
    deleted_methods = []
77
    transferred_methods = []
78
    new_methods = []
79
80
    # Finding all transferred and deleted methods
81
    for before_method in before_methods:
82
        transferred = False
83
        for after_method in after_methods:
84
            if before_method.name == after_method.name:  # Match found
85
                transferred_methods.append((before_method, after_method))
86
                transferred = True
87
                break
88
        if not transferred:
89
            deleted_methods.append(before_method)
90
    # Finding all new methods
91
    for after_method in after_methods:
92
        transferred = False
93
        for _, am in transferred_methods:
94
            if after_method == am:
95
                transferred = True
96
                break
97
        if not transferred:
98
            new_methods.append(after_method)
99
100
    return deleted_methods, transferred_methods, new_methods
101
    
102
103
def process_file(source_before, source_after, additions, deletions): 
104
    rows = []
105
    # Counting from 1, because source code lines are counted from there
106
    # NOTE: Deletions komen wel degelijk goed binnen
107
    #print("deletions:")
108
    #print(deletions)
109
    before_count = 1
110
    after_count = 1
111
    while True:
112
        row = dict()
113
        #print("PROCESS_FILE")
114
        #print(deletions)
115
        #print(additions)
116
        # We check the deletion lines first; if a line was "changed", the
117
        # 'deleted' line needs to be listed first, and after that, the 'added'
118
        # line
119
        #print(str(before_count) + " -- " + str(after_count))
120
        if before_count in deletions:
121
            row["type"] = "deletion"
122
            row["old_num"] = before_count
123
            # Why source_before[] and not deletions[]?
124
            # Because source_before[] has been processed by Pygments, and thus
125
            # has the syntax highlighting in it. deletions[] is just plain text.
126
127
128
            # Look, I'm tired at the time of writing, and this line seems to be
129
            # kind of a dick so I'm just gonna try catch it and leave it like
130
            # that for now
131
            #try: 
132
                #row["line"] = source_before[before_count]
133
            #print(len(source_before))
134
            #print(before_count)
135
            if before_count == len(source_before):
136
                row["line"] = source_before[-1]
137
            else:
138
                row["line"] = source_before[before_count]
139
            #except:
140
                #pass
141
142
            before_count += 1
143
        elif after_count in additions:
144
            row["type"] = "addition"
145
            row["new_num"] = after_count
146
            # Idem for additions[]
147
            if after_count == len(source_after):
148
                row["line"] = source_after[-1]
149
            else:
150
                row["line"] = source_after[after_count]
151
            after_count += 1
152
        else:  # No change to this particular line
153
            row["old_num"] = before_count
154
            row["new_num"] = after_count
155
            if len(source_before) > before_count:
156
                row["line"] = source_before[before_count] 
157
            elif len(source_after) > after_count:
158
                row["line"] = source_after[after_count] 
159
            else:  # No lines left
160
                break
161
            # If not end of both files, increment both counters anyway
162
            before_count += 1
163
            after_count += 1
164
        # Adding the new row to the list of rows
165
        rows.append(row)
166
    return rows
167
168
169
170
171
172
        
173
174
    
175