getgrit / gritql

GritQL is a query language for searching, linting, and modifying code.
https://docs.grit.io/
MIT License
2.78k stars 56 forks source link

Alternative diff output #263

Open morgante opened 2 months ago

morgante commented 2 months ago

Some users like to customize their git diff output.

In theory, grit apply is very similar and we should be able to hook into a pluggable diff output format. Ex. ripgrep seems to work with delta here: https://dandavison.github.io/delta/grep.html

noahbald commented 2 months ago

@morgante Not sure I'll be able to help too much on this one as it might be a bit more heft than I can handle at this point. Here's what I've been able to find so far regarding this issue though.

From what I've found Delta works by parsing the output of a stream. If we choose to get things working with other pagers such as Delta then we need to (or at least ned to have the option to) output contents the same as git diff and rg --json does.

git diff filename.txt | delta

Starting with the output of diffs, we can compare our output to that of git.

diff --git a/package.json b/package.json
index 2b3133b..af4a1f8 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
 {
-  "name": "@getgrit/gritql",
+  "name": "DELETE_ME",
   "version": "0.2.2",
   "description": "Core GritQL engine and CLI",
   "author": "",

For readability we need at least the following

Let's compare this with our current output

example.js
    -const myVar = 2;
    +let myVar = 2;

Processed 1 files and found 1 matches

It's fine to keep the message at the end of the diff. It doesn't start with -, +, or so it doesn't format it.


Now, taking a look at match outputs as a grep format, Delta expects an output formatted like rg --json -C 2 "my-query"

{ "type": "begin", "data": { "path": { "text": "package.json" } } },
{
  "type": "context",
  "data": {
    "path": { "text": "package.json" },
    "lines": { "text": "{\n" },
    "line_number": 1,
    "absolute_offset": 0,
    "submatches": []
  }
},
{
  "type": "match",
  "data": {
    "path": { "text": "package.json" },
    "lines": { "text": "  \"name\": \"DELETE_ME\",\n" },
    "line_number": 2,
    "absolute_offset": 2,
    "submatches": [
      { "match": { "text": "DELETE_ME" }, "start": 11, "end": 20 }
    ]
  }
},
// ...more match/context blocks
{
  "type": "end",
  "data": {
    "path": { "text": "grep-example.txt" },
    "binary_offset": null,
    "stats": {
      "elapsed": { "secs": 0, "nanos": 1656401, "human": "0.001656s" },
      "searches": 1,
      "searches_with_match": 1,
      "bytes_searched": 1260,
      "bytes_printed": 1736,
      "matched_lines": 1,
      "matches": 2
    }
  }
},
// ...more start/end blocks with match/context blocks between
{
  "data": {
    "elapsed_total": { "human": "2.397146s", "nanos": 397145746, "secs": 2 },
    "stats": {
      "bytes_printed": 2477,
      "bytes_searched": 1710,
      "elapsed": { "human": "0.003502s", "nanos": 3502102, "secs": 0 },
      "matched_lines": 2,
      "matches": 3,
      "searches": 2,
      "searches_with_match": 2
    }
  },
  "type": "summary"
}

From messing with this output, it seems the only thing we can omit is the following


Approaches

There a many ways we could approach this, and we might want to discuss the most appropriate method to do this. We would need to update the formatter to match git diff/grep --json either when

  1. When any output is made;
  2. When an option to --output (eg --output pager) to format according to the needs of Delta;
  3. When grit's stdout is sent to a pipe, format according to the needs of Delta; or
  4. When a configuration option to use a pager such as delta. When given, pipe the output to the specified executable, in the format Delta expects

Caveats

morgante commented 2 months ago

Thanks for investigating this!

Overall, I think I like the option of starting with an --output=diff flag which will match git diff output. This should be fairly portable, and if I understand correctly, would allow piping into Delta easily.

Once that's implemented, we could consider an --pager flag to handle the piping ourselves.

Unfortunately, grit doesn't know how to handle diffs and matches being passed to it side-by-side. So the pager may have to be run multiple times as we switch from type to type.

Do you mean that delta doesn't support this? For initial implementation, I assume we would only really be interested in diffs.

noahbald commented 2 months ago

Yeah what I mean is that we can't mix diffs and grep outputs together. Each type would have to be processed by delta separately.