diff options
Diffstat (limited to 'doc/development/merge_request_concepts/diffs/frontend.md')
-rw-r--r-- | doc/development/merge_request_concepts/diffs/frontend.md | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/doc/development/merge_request_concepts/diffs/frontend.md b/doc/development/merge_request_concepts/diffs/frontend.md new file mode 100644 index 00000000000..6bd6d80af94 --- /dev/null +++ b/doc/development/merge_request_concepts/diffs/frontend.md @@ -0,0 +1,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. |