widgets.diff: simplify DiffEditor.has_selection()

When I added the DiffEditor.has_selection() method, I missed the fact
that QTextCursor has a hasSelection() method.  Simplify
DiffEditor.has_selection() by reimplmenting it in terms of
QTextCursor.hasSelection().

Signed-off-by: Daniel Harding <dharding@living180.net>
diffparse: rewrite with improved accuracy

Previously, the DiffParser.process_diff_selection() method took the
contents of the text selection and searched for it in the output of git
diff to determine which subset of the diff to process.  Most of the time
this worked, but if only a small portion of the diff was selected, such
that the selection appeared multiple times in the diff, the wrong part
of the diff might be processed.  This is especially bad when performing
a Revert operation, because the user can lose data if reverting the
wrong lines.

This commit provides a complete rewrite of the cola.diffparse module
such that the Stage/Unstage/Revert operations should (modulo any bugs)
always process exactly the line(s) selected by the user.

Following is a summary of the changes, starting from the DiffParser
class and working outward:

Simplify the purpose of the DiffParser class.  It no longer knows
anything about models or how to apply changes to the index or the
worktree.  Nor does it call out to git anymore to load any data about
the diff.  The initializer now takes just two parameters:  the filename
(or relative path) of the file being diffed, and the actual text of the
diff (without the diff header).  The initializer uses a new standalone
function _parse_diff() to convert the diff text to a list of _DiffHunk
objects, which are stored on the instance in the hunks attribute.
DiffParser now has just two methods, the first of which is
generate_patch().  It takes the indexes of the first and last lines of
the diff to include in the patch, along with an optional boolean flag
indicating if a reverse patch should be generated.  It returns a string
containing the patch (including the diff header, generated using the
filename passed to the initializer), or None if the resulting patch
would not change anything.  The second method is generate_hunk_patch(),
which is a simple wrapper around the generate_hunk() method.  It takes a
single line index along with a reverse flag, and generates patch using
only the hunk containing the specified line.

Next is the ApplyDiffSelection command class, which is the sole user of
the DiffParser class.  Update the initializer to replace the offset and
selection_text parameters (indicating the start and contents of the
selection, respectively), with three parameters indicating the indexes
of the first and last selected lines (or of the line containing the
cursor if there is no selection), along with a boolean indicating if
there is actually any text selected or not (to differentiate between a
selection on a single line versus no selection at all).  Merge the
functionality of the old DiffParser.process_diff_selection() method into
the do() method.  This method now first constructs a DiffParser
instance, passing it the filename and diff text from the model.  It then
uses the has_selection attribute to call either the generate_patch() or
the generate_hunk_patch() method of the DiffParser instance, depending
on whether or not there is a selection.  Finally, if the resulting patch
is not None, it writes it to a temporary file which is passed to either
the model's apply_diff_to_worktree() or apply_diff() methods, depending
on the operation being performed (Revert vs. Stage/Unstage).  It then
logs the status and updates the model as before.

Lastly, update the DiffEditor widget class.  Add a selected_lines()
method, which returns the indexes of the first and last selected lines
(or of the line containing the cursor if there is no selection).  Update
the process_diff_selection() method to use this new method along with
the has_selection() method to pass the appropriate line indexes and the
has_selection flag when constructing the ApplyDiffSelection command.
Also, to reflect the change to ApplyDiffSelection, replace the staged
parameter with a reverse parameter, and update the callers of the
process_diff_selection() method appropriately.

In addition, update the tests to reflect these changes, and remove the
diff-*-reverse.txt fixtures as they are no longer needed (the code now
converts the forward diff to a reverse diff when necessary).

Signed-off-by: Daniel Harding <dharding@living180.net>
Merge pull request #399 from living180/diffparser_work

diff: rewrite DiffParser

Signed-off-by: David Aguilar <davvid@gmail.com>