eric6/Plugins/VcsPlugins/vcsMercurial/HgDiffGenerator.py

changeset 6942
2602857055c5
parent 6891
93f82da09f22
child 7192
a22eee00b052
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2015 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a class to generate the output of the hg diff command.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode
13 except NameError:
14 pass
15
16 import os
17
18 from PyQt5.QtCore import pyqtSignal, QProcess, QTimer, QObject
19
20
21 class HgDiffGenerator(QObject):
22 """
23 Class implementing the generation of output of the hg diff command.
24
25 @signal finished() emitted when all processes have finished
26 """
27 finished = pyqtSignal()
28
29 def __init__(self, vcs, parent=None):
30 """
31 Constructor
32
33 @param vcs reference to the vcs object
34 @param parent parent widget (QWidget)
35 """
36 super(HgDiffGenerator, self).__init__(parent)
37
38 self.vcs = vcs
39
40 self.__hgClient = self.vcs.getClient()
41 if self.__hgClient:
42 self.process = None
43 else:
44 self.process = QProcess()
45 self.process.finished.connect(self.__finish)
46 self.process.readyReadStandardOutput.connect(self.__readStdout)
47 self.process.readyReadStandardError.connect(self.__readStderr)
48
49 def stopProcess(self):
50 """
51 Public slot to stop the diff process.
52 """
53 if self.__hgClient:
54 if self.__hgClient.isExecuting():
55 self.__hgClient.cancel()
56 else:
57 if self.process is not None and \
58 self.process.state() != QProcess.NotRunning:
59 self.process.terminate()
60 QTimer.singleShot(2000, self.process.kill)
61 self.process.waitForFinished(3000)
62
63 def __getVersionArg(self, version):
64 """
65 Private method to get a hg revision argument for the given revision.
66
67 @param version revision (integer or string)
68 @return version argument (string)
69 """
70 if version == "WORKING":
71 return None
72 else:
73 return str(version)
74
75 def start(self, fn, versions=None, bundle=None, qdiff=False):
76 """
77 Public slot to start the hg diff command.
78
79 @param fn filename to be diffed (string)
80 @keyparam versions list of versions to be diffed (list of up to
81 2 strings or None)
82 @keyparam bundle name of a bundle file (string)
83 @keyparam qdiff flag indicating qdiff command shall be used (boolean)
84 @return flag indicating a successful start of the diff command
85 (boolean)
86 """
87 if qdiff:
88 args = self.vcs.initCommand("qdiff")
89 else:
90 args = self.vcs.initCommand("diff")
91
92 if self.vcs.hasSubrepositories():
93 args.append("--subrepos")
94
95 if bundle:
96 args.append('--repository')
97 args.append(bundle)
98 elif self.vcs.bundleFile and os.path.exists(self.vcs.bundleFile):
99 args.append('--repository')
100 args.append(self.vcs.bundleFile)
101
102 if versions is not None:
103 rev1 = self.__getVersionArg(versions[0])
104 rev2 = None
105 if len(versions) == 2:
106 rev2 = self.__getVersionArg(versions[1])
107
108 if rev1 is not None or rev2 is not None:
109 args.append('-r')
110 if rev1 is not None and rev2 is not None:
111 args.append('{0}:{1}'.format(rev1, rev2))
112 elif rev2 is None:
113 args.append(rev1)
114 elif rev1 is None:
115 args.append(':{0}'.format(rev2))
116
117 if isinstance(fn, list):
118 dname, fnames = self.vcs.splitPathList(fn)
119 self.vcs.addArguments(args, fn)
120 else:
121 dname, fname = self.vcs.splitPath(fn)
122 args.append(fn)
123
124 self.__oldFile = ""
125 self.__oldFileLine = -1
126 self.__fileSeparators = []
127 self.__output = []
128 self.__errors = []
129
130 if self.__hgClient:
131 out, err = self.__hgClient.runcommand(args)
132
133 if err:
134 self.__errors = err.splitlines(True)
135
136 if out:
137 for line in out.splitlines(True):
138 self.__processOutputLine(line)
139 if self.__hgClient.wasCanceled():
140 break
141
142 self.__finish()
143 else:
144 # find the root of the repo
145 repodir = dname
146 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
147 repodir = os.path.dirname(repodir)
148 if os.path.splitdrive(repodir)[1] == os.sep:
149 return False
150
151 self.process.kill()
152 self.process.setWorkingDirectory(repodir)
153
154 self.process.start('hg', args)
155 procStarted = self.process.waitForStarted(5000)
156 if not procStarted:
157 return False
158
159 return True
160
161 def __finish(self):
162 """
163 Private slot called when the process finished or the user pressed
164 the button.
165 """
166 self.finished.emit()
167
168 def getResult(self):
169 """
170 Public method to return the result data.
171
172 @return tuple of lists of string containing lines of the diff, the
173 list of errors and a list of tuples of filenames and the line
174 into the diff output.
175 """
176 return (self.__output, self.__errors, self.__fileSeparators)
177
178 def __extractFileName(self, line):
179 """
180 Private method to extract the file name out of a file separator line.
181
182 @param line line to be processed (string)
183 @return extracted file name (string)
184 """
185 f = line.split(None, 1)[1]
186 f = f.rsplit(None, 6)[0]
187 if f == "/dev/null":
188 f = "__NULL__"
189 else:
190 f = f.split("/", 1)[1]
191 return f
192
193 def __processFileLine(self, line):
194 """
195 Private slot to process a line giving the old/new file.
196
197 @param line line to be processed (string)
198 """
199 if line.startswith('---'):
200 self.__oldFileLine = len(self.__output)
201 self.__oldFile = self.__extractFileName(line)
202 else:
203 newFile = self.__extractFileName(line)
204 if self.__oldFile == "__NULL__":
205 self.__fileSeparators.append(
206 (newFile, newFile, self.__oldFileLine))
207 else:
208 self.__fileSeparators.append(
209 (self.__oldFile, newFile, self.__oldFileLine))
210
211 def __processOutputLine(self, line):
212 """
213 Private method to process the lines of output.
214
215 @param line output line to be processed (string)
216 """
217 if line.startswith("--- ") or \
218 line.startswith("+++ "):
219 self.__processFileLine(line)
220
221 self.__output.append(line)
222
223 def __readStdout(self):
224 """
225 Private slot to handle the readyReadStandardOutput signal.
226
227 It reads the output of the process, formats it and inserts it into
228 the contents pane.
229 """
230 self.process.setReadChannel(QProcess.StandardOutput)
231
232 while self.process.canReadLine():
233 line = str(self.process.readLine(), self.vcs.getEncoding(),
234 'replace')
235 self.__processOutputLine(line)
236
237 def __readStderr(self):
238 """
239 Private slot to handle the readyReadStandardError signal.
240
241 It reads the error output of the process and inserts it into the
242 error pane.
243 """
244 if self.process is not None:
245 s = str(self.process.readAllStandardError(),
246 self.vcs.getEncoding(), 'replace')
247 self.__errors.append(s)

eric ide

mercurial