Welcome to mirror list, hosted at ThFree Co, Russian Federation.

frontend.md « diffs « merge_request_concepts « development « doc - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 6bd6d80af945d5540f33b945ab261e10d383ba22 (plain)
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
---
stage: Create
group: Code Review
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

# Merge request diffs frontend overview

This document provides an overview on how the frontend diffs Vue application works, and
the various different parts that exist. It should help contributors:

- Understand how the diffs Vue app is set up.
- Identify any areas that need improvement.

This document is a living document. Update it whenever anything significant changes in
the diffs application.

## Diffs Vue app

### Components

The Vue app for rendering diffs uses many different Vue components, some of which get shared
with other areas of the GitLab app. The below chart shows the direction for which components
get rendered.

NOTE:
[Issue #388843](https://gitlab.com/gitlab-org/gitlab/-/issues/388843) is open to
generate a Mermaid graph of the components diagram. An image version of the
diagram is available in the issue.

Some of the components are rendered more than others, but the main component is `diff_row.vue`.
This component renders every diff line in a diff file. For performance reasons, this
component is a functional component. However, when we upgrade to Vue 3, this is no longer
required.

The main diff app component is the main entry point to the diffs app. One of the most important parts
of this component is to dispatch the action that assigns discussions to diff lines. This action
gets dispatched after the metadata request is completed, and after the batch diffs requests are
finished. There is also a watcher set up to watches for changes in both the diff files array and the notes
array. Whenever a change happens here, the set discussion action gets dispatched.

The DiffRow component is set up in a way that allows for us to store the diff line data in one format.
Previously, we had to request two different formats for inline and side-by-side. The DiffRow component
then uses this standard format to render the diff line data. With this standard format, the user
can then switch between inline and side-by-side without the need to re-fetch any data.

NOTE:
For this component, a lot of the data used and rendered gets memoized and cached, based on
various conditions. It is possible that data sometimes gets cached between each different
component render.

### Vuex store

The Vuex store for the diffs app consists of 3 different modules:

- Notes
- Diffs
- Batch comments

The notes module is responsible for the discussions, including diff discussions. In this module,
the discussions get fetched, and the polling for new discussions is setup. This module gets shared
with the issue app as well, so changes here need to be tested in both issues and merge requests.

The diffs module is responsible for the everything related to diffs. This includes, but is not limited
to, fetching diffs, assigning diff discussions to lines, and creating diff discussions.

Finally, the batch comments module is not complex, and is responsible only for the draft comments feature.
However, this module does dispatch actions in the notes and diff modules whenever draft comments
are published.

### API Requests

#### Metadata

The diffs metadata endpoint exists to fetch the base data the diffs app requires quickly, without
the need to fetch all the diff files. This includes, but is not limited to:

- Diff file names, including some extra meta data for diff files
- Added and removed line numbers
- Branch names
- Diff versions

The most important part of the metadata response is the diff file names. This data allows the diffs
app to render the file browser inside of the diffs app, without waiting for all batch diffs
requests to complete.

When the metadata response is received, the diff file data gets sent to a web worker. The web worker
exists to allow for this data, which for larger merge requests could be huge, to be processed off
the main thread. Processing this data involves getting the data into the correct structure
that the frontend requires to render the file browser in either tree view or list view.

```mermaid
graph TD
    A[fetchDiffFilesMeta]
    B[Create web worker]
    C[Fetch data]
    D[SET_DIFF_METADATA]
    E[Post worker data]
    F[Worker message event listener]
    K[SET_TREE_DATA]

    G[TreeWorker]
    H[onMessage]
    I[generateTreeList]
    J[postMessage sortTree]

    A -->B
    E -->F
    B -->C
    C -->D
    D -->E

    G -->H
    H -->I
    I -->J
    J -->F

    F -->K
```

The structure for this file object is:

```javascript
{
  "key": "",
  "path": "",
  "name": "",
  "type": "",
  "tree": [],
  "changed": true,
  "tempFile": false,
  "deleted": false,
  "fileHash": "",
  "addedLines": 1,
  "removedLines": 1,
  "parentPath": "/",
  "submodule": false
}
```

#### Batch diffs

To reduce the response size for the diffs endpoint, we are splitting this response up into different
requests, to:

- Reduces the response size of each request.
- Allows the diffs app to start rendering diffs as quickly as the first request finishes.

To make the first request quicker, the request gets sent asking for a small amount of
diffs. The number of diffs requested then increases, until the maximum number of diffs per request is 30.

When the request finishes, the diffs app formats the data received into a format that makes
it easier for the diffs app to render the diffs lines.

```mermaid
graph TD
    A[fetchDiffFilesBatch] -->
    B[commit SET_DIFF_DATA_BATCH] -->
    C[prepareDiffData] -->
    D[prepareRawDiffFile] -->
    E[ensureBasicDiffFileLines] -->
    F[prepareDiffFileLines] -->
    G[finalizeDiffFile] -->
    H[deduplicateFilesList]
```

After this has been completed, the diffs app can now begin to render the diff lines. However, before
anything can be rendered the diffs app does one more format. It takes the diff line data, and maps
the data into a format for easier switching between inline and side-by-side modes. This
formatting happens in a computed property inside the `diff_content.vue` component.

### Render queue

NOTE:
This _might_ not be required any more. Some investigation work is required to decide
the future of the render queue. The virtual scroll bar we created has probably removed
any performance benefit we got from this approach.

To render diffs quickly, we have a render queue that allows the diffs to render only if the
browser is idle. This saves the browser getting frozen when rendering a lot of large diffs at once,
and allows us to reduce the total blocking time.

This pipeline of rendering files happens only if all the below conditions are `true` for every
diff file. If any of these are `false`, then this render queue does not happen and the diffs get
rendered as expected.

- Are the diffs in this file already rendered?
- Does this diff have a viewer? (Meaning, is it not a download?)
- Is the diff expanded?

This chart gives a brief overview of the pipeline that happens:

```mermaid
graph TD
    A[startRenderDiffsQueue] -->B
    B[commit RENDER_FILE current file index] -->C
    C[canRenderNextFile?]
    C -->|Yes| D[Render file] -->B
    C -->|No| E[Re-run requestIdleCallback] -->C
```

The checks that happen:

- Is the idle time remaining less than 5 ms?
- Have we already tried to render this file 4 times?

After these checks happen, the file is marked in Vuex as `renderable`, which allows the diffs
app to start rendering the diff lines and discussions.