[qt] open real file in editor if working directory is selected

Related to #137719

Usecase:

  1. User up on a changeset then opens a file for a fix, saves the file, amends the change set, etc.
  2. User is currently on a dirty working directory and opens a file that is already modified, etc.

There are 2 cases:

clean working directory:selection should be on the working changeset
dirty working direction:selection should be on the row that display the dirty working changeset

How to test

cd /tmp
hg init testhgview
cd testhgview
echo babar > babar
hg add babar
hg ci -m babar
echo celestine >> babar
hg ci -m celestine

hg --config hgview.editor=emacs qv -I qt&

Select revision 0, "open in editor" opens a temporary file. Select revision 1, "open in editor" opens the real file.

echo celestine > babar

Select revision 1, "open in editor" opens a temporary file. Select durty working directory, "open in editor" opens the real file.

authorAlain Leufroy <alain@leufroy.fr>
changeseta759dda22b17
branchdefault
phasepublic
hiddenno
parent revision#2d442ca81cb9 [qt4] add an icon for open in editor
child revision#864c54d9fdfc [qt] start editor as a detached process
files modified by this revision
hgviewlib/qt4/hgfileview.py
# HG changeset patch
# User Alain Leufroy <alain@leufroy.fr>
# Date 1370651598 -7200
# Sat Jun 08 02:33:18 2013 +0200
# Node ID a759dda22b178ca79e6ee34dcdfd08453152ba3c
# Parent 2d442ca81cb9bcc4c868e261bab168f0a6319473
[qt] open real file in editor if working directory is selected

Related to #137719

Usecase:

1. User up on a changeset then opens a file for a fix, saves the
file, amends the change set, etc.

2. User is currently on a dirty working directory and opens a file
that is already modified, etc.

There are 2 cases:

:clean working directory: selection should be on the working changeset

:dirty working direction: selection should be on the row that display
the dirty working changeset

.. admonition:: How to test

::

cd /tmp
hg init testhgview
cd testhgview
echo babar > babar
hg add babar
hg ci -m babar
echo celestine >> babar
hg ci -m celestine

hg --config hgview.editor=emacs qv -I qt&

Select revision 0, "open in editor" opens a temporary file.
Select revision 1, "open in editor" opens the real file.

::

echo celestine > babar

Select revision 1, "open in editor" opens a temporary file.
Select durty working directory, "open in editor" opens the real file.

diff --git a/hgviewlib/qt4/hgfileview.py b/hgviewlib/qt4/hgfileview.py
@@ -450,16 +450,17 @@
1          return True
2 
3      def openEditor(self):
4          """Open the editor with the content of the selected file at
5          the selected revision"""
6 -        _, content = self._model.graph.filedata(self._filename, self._ctx.rev(),
7 -                                                'file', maxfilesize=-1)
8 -        suffix = '_%s-%s-%s' % (self._ctx.rev(), str(self._ctx),
9 -                               os.path.basename(self._filename))
10 -        open_tempfile_in_editor(self, self.cfg.getEditor(), content,
11 -                                suffix=suffix)
12 +        # We open the current file if the selected revision is the dirty working
13 +        # directory or if it is the working directory without any modification.
14 +        # Else we use a temporary file.
15 +        content_getter = lambda: self._model.graph.filedata(
16 +            self._filename, self._ctx.rev(), 'file', maxfilesize=-1)[1]
17 +        _open_in_editor(self, self.cfg, self._ctx.filectx(self._filename),
18 +                        content_getter)
19 
20      def updateDiffDecorations(self):
21          """
22          Recompute the diff and starts the timer
23          responsible for filling diff decoration markers
@@ -684,13 +685,13 @@
24          """Open the editor with the content of the selected file at
25          the selected revision"""
26          index = self.currentIndex()
27          sel_file = self.model().fileFromIndex(index)
28          from_rev = self.model().current_ctx.rev()
29 -        content = self.model().repo[from_rev].filectx(sel_file).data()
30 -        open_tempfile_in_editor(self, HgConfig(self.model().repo.ui).getEditor(),
31 -                                content, suffix=os.path.basename(sel_file))
32 +        filectx = self.model().repo[from_rev].filectx(sel_file)
33 +        _open_in_editor(self, HgConfig(self.model().repo.ui),
34 +                        filectx, filectx.data)
35 
36      def navigate(self, filename=None):
37          self._navigate(filename, FileViewer, self._nav_dialogs)
38 
39      def diffNavigate(self, filename=None):
@@ -767,14 +768,52 @@
40      def prevFile(self):
41          row = self.currentIndex().row()
42          self.setCurrentIndex(self.model().index(max(row - 1, 0), 0))
43 
44 
45 -def open_tempfile_in_editor(parent, editor, content, suffix=None):
46 -    """Open the editor with the given content in a temporary file."""
47 +def _open_in_editor(parent, cfg, filectx, content_getter):
48 +    """Open the editor on the file at the context ``filectx``.
49 +
50 +    If the selected revision is the working directory then the original file
51 +    opened else a temporary file is created.
52 +    """
53 +    editor = cfg.getEditor()
54 +    if filectx.rev() is None: # dirty wd is selected
55 +        return _open_originalfile_in_editor(parent, editor, filectx)
56 +    if ((not filectx._repo[None].dirty()) and                    # the wd is clean and
57 +        (filectx.changectx() in filectx._repo[None].parents())): # a wd parent is selected
58 +        return _open_originalfile_in_editor(parent, editor, filectx)
59 +    content = content_getter()
60 +    suffix = '_%s-%s-%s' % (filectx.rev(), str(filectx.changectx()),
61 +                            os.path.basename(filectx.path()))
62 +    _open_tempfile_in_editor(parent, editor, content, suffix=suffix)
63 +
64 +
65 +def _open_originalfile_in_editor(parent, editor, filectx):
66 +    """Open the ``editor`` on the original file from filectx"""
67 +    filepath = os.path.join(
68 +        os.path.abspath(filectx._repo.root),
69 +        filectx.path()
70 +        )
71 +    return _open_file_in_editor(parent, editor, filepath)
72 +
73 +
74 +def _open_tempfile_in_editor(parent, editor, content, suffix=None):
75 +    """Open the ``editor`` with the given ``content`` in a temporary file.
76 +
77 +    ``suffix`` is the suffix for the temporary file name.
78 +    ``parent`` is a Qt component that gets a reference to the editor process.
79 +    """
80      fid, filepath = tempfile.mkstemp(suffix=suffix)
81      os.close(fid)
82      with open(filepath, 'w') as fid:
83          fid.write(content)
84 +    process = _open_file_in_editor(parent, editor, filepath)
85 +    process.finished.connect(lambda rt, st: os.remove(filepath))
86 +
87 +def _open_file_in_editor(parent, editor, filepath):
88 +    """Open the ``filepath`` using the ``editor``.
89 +    ``parent`` is a Qt component that get a reference to the editor process.
90 +    """
91      process = QtCore.QProcess(parent)
92      process.start(editor, [filepath])
93 -    process.finished.connect(lambda rt, st: os.remove(filepath))
94 +    return process