forked from kemayo/sublime-text-git
-
Notifications
You must be signed in to change notification settings - Fork 0
/
diff.py
167 lines (122 loc) · 5.51 KB
/
diff.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import sublime
import re
from git import git_root, GitTextCommand, GitWindowCommand
import functools
def do_when(conditional, callback, *args, **kwargs):
if conditional():
return callback(*args, **kwargs)
sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50)
def goto_xy(view, line, col):
view.run_command("goto_line", {"line": line})
for i in range(col):
view.run_command("move", {"by": "characters", "forward": True})
class GitDiff (object):
def run(self, edit=None):
self.run_command(['git', 'diff', '--no-color', '--', self.get_file_name()],
self.diff_done)
def diff_done(self, result):
if not result.strip():
self.panel("No output")
return
s = sublime.load_settings("Git.sublime-settings")
if s.get('diff_panel'):
view = self.panel(result)
else:
view = self.scratch(result, title="Git Diff")
lines_inserted = view.find_all(r'^\+[^+]{2} ')
lines_deleted = view.find_all(r'^-[^-]{2} ')
view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN)
view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN)
# Store the git root directory in the view so we can resolve relative paths
# when the user wants to navigate to the source file.
view.settings().set("git_root_dir", git_root(self.get_working_dir()))
class GitDiffCommit (object):
def run(self, edit=None):
self.run_command(['git', 'diff', '--cached', '--no-color'],
self.diff_done)
def diff_done(self, result):
if not result.strip():
self.panel("No output")
return
self.scratch(result, title="Git Diff")
class GitDiffCommand(GitDiff, GitTextCommand):
pass
class GitDiffAllCommand(GitDiff, GitWindowCommand):
pass
class GitDiffCommitCommand(GitDiffCommit, GitWindowCommand):
pass
class GitDiffTool(object):
def run(self, edit=None):
self.run_command(['git', 'difftool', '--', self.get_file_name()])
class GitDiffToolCommand(GitDiffTool, GitTextCommand):
pass
class GitDiffToolAll(GitWindowCommand):
def run(self):
self.run_command(['git', 'difftool'])
class GitDiffToolCommit(GitTextCommand):
def run(self, edit=None):
self.run_command(['git', 'difftool', '--cached', '--', self.get_file_name()])
class GitDiffToolCommitAll(GitWindowCommand):
def run(self):
self.run_command(['git', 'difftool', '--cached'])
class GitGotoDiff(sublime_plugin.TextCommand):
def run(self, edit):
v = self.view
view_scope_name = v.scope_name(v.sel()[0].a)
scope_markup_inserted = ("markup.inserted.diff" in view_scope_name)
scope_markup_deleted = ("markup.deleted.diff" in view_scope_name)
if not scope_markup_inserted and not scope_markup_deleted:
return
beg = v.sel()[0].a # Current position in selection
pt = v.line(beg).a # First position in the current diff line
self.column = beg - pt - 1 # The current column (-1 because the first char in diff file)
self.file_name = None
hunk_line = None
line_offset = 0
while pt > 0:
line = v.line(pt)
lineContent = v.substr(line)
if lineContent.startswith("@@"):
if not hunk_line:
hunk_line = lineContent
elif lineContent.startswith("+++ b/"):
self.file_name = v.substr(sublime.Region(line.a+6, line.b)).strip()
break
elif not hunk_line and not lineContent.startswith("-"):
line_offset = line_offset+1
pt = v.line(pt-1).a
hunk = re.match(r"^@@ -(\d+),(\d+) \+(\d+),(\d+) @@.*", hunk_line)
if not hunk:
sublime.status_message("No hunk info")
return
hunk_start_line = hunk.group(3)
self.goto_line = int(hunk_start_line) + line_offset - 1
git_root_dir = v.settings().get("git_root_dir")
# Sanity check and see if the file we're going to try to open even
# exists. If it does not, prompt the user for the correct base directory
# to use for their diff.
full_path_file_name = self.file_name
if git_root_dir:
full_path_file_name = os.path.join(git_root_dir, self.file_name)
else:
git_root_dir = ""
if not os.path.isfile(full_path_file_name):
caption = "Enter base directory for file '%s':" % self.file_name
v.window().show_input_panel(caption,
git_root_dir,
self.on_path_confirmed,
None,
None)
else:
self.on_path_confirmed(git_root_dir)
def on_path_confirmed(self, git_root_dir):
v = self.view
old_git_root_dir = v.settings().get("git_root_dir")
# If the user provided a new git_root_dir, save it in the view settings
# so they only have to fix it once
if old_git_root_dir != git_root_dir:
v.settings().set("git_root_dir", git_root_dir)
full_path_file_name = os.path.join(git_root_dir, self.file_name)
new_view = v.window().open_file(full_path_file_name)
do_when(lambda: not new_view.is_loading(),
lambda: goto_xy(new_view, self.goto_line, self.column))