Skip to content

Commit c522710

Browse files
committed
Use codemirror merge addon to replace mis-merge
1 parent 5e26801 commit c522710

File tree

10 files changed

+540
-73
lines changed

10 files changed

+540
-73
lines changed

packages/insomnia/package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@
4040
"@apideck/better-ajv-errors": "^0.3.6",
4141
"@apidevtools/swagger-parser": "10.1.1",
4242
"@bufbuild/protobuf": "^1.10.0",
43-
"@connectrpc/connect-node": "^1.6.1",
4443
"@connectrpc/connect": "^1.6.1",
44+
"@connectrpc/connect-node": "^1.6.1",
4545
"@faker-js/faker": "9.7.0",
4646
"@fortawesome/fontawesome-svg-core": "^6.7.2",
4747
"@fortawesome/free-brands-svg-icons": "^6.7.2",
@@ -71,26 +71,27 @@
7171
"autoprefixer": "^10.4.21",
7272
"aws4": "^1.13.2",
7373
"buffer": "^6.0.3",
74-
"chai-json-schema": "1.5.1",
7574
"chai": "^4.5.0",
75+
"chai-json-schema": "1.5.1",
7676
"chardet": "^2.1.0",
7777
"classnames": "^2.5.1",
7878
"clone": "^2.1.2",
79-
"codemirror-graphql": "^2.2.0",
8079
"codemirror": "^5.65.19",
80+
"codemirror-graphql": "^2.2.0",
8181
"color": "^4.2.3",
8282
"content-disposition": "^0.5.4",
8383
"cookie-parser": "^1.4.7",
8484
"date-fns": "^3.6.0",
8585
"decompress": "^4.2.1",
8686
"deep-equal": "^1.1.2",
87+
"diff-match-patch-ts": "^0.6.0",
8788
"dompurify": "^3.2.5",
8889
"electron-context-menu": "^3.6.1",
8990
"electron-updater": "^6.6.2",
9091
"fastq": "^1.19.1",
9192
"fuzzysort": "^1.9.0",
92-
"graphql-ws": "^5.16.2",
9393
"graphql": "^16.10.0",
94+
"graphql-ws": "^5.16.2",
9495
"grpc-reflection-js": "Kong/grpc-reflection-js#master",
9596
"gsap": "^3.12.7",
9697
"hawk": "9.0.2",
@@ -121,14 +122,14 @@
121122
"oauth-1.0a": "^2.2.6",
122123
"objectpath": "^2.0.0",
123124
"papaparse": "^5.5.2",
124-
"react-aria-components": "^1.12.2",
125+
"react": "^18.3.1",
125126
"react-aria": "3.43.2",
127+
"react-aria-components": "^1.12.2",
126128
"react-dom": "^18.3.1",
127129
"react-resizable-panels": "^3.0.6",
128130
"react-router": "^7.9.4",
129131
"react-stately": "3.41.0",
130132
"react-use": "^17.6.0",
131-
"react": "^18.3.1",
132133
"shell-quote": "^1.8.2",
133134
"socket.io-client": "^4.8.1",
134135
"swagger-ui-dist": "^5.21.0",

packages/insomnia/src/ui/components/.client/codemirror/base-imports.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import 'codemirror/addon/selection/active-line';
3333
import 'codemirror/addon/selection/selection-pointer';
3434
import 'codemirror/addon/display/placeholder';
3535
import 'codemirror/addon/lint/lint';
36+
import 'codemirror/addon/merge/merge.js';
3637

3738
// for the code that uses this yaml parser, see https://github.com/codemirror/CodeMirror/blob/master/addon/lint/yaml-lint.js
3839
import * as jsyaml from 'js-yaml';
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import './base-imports';
2+
3+
import classnames from 'classnames';
4+
import CodeMirror from 'codemirror';
5+
import type { MergeView } from 'codemirror/addon/merge/merge';
6+
import { DiffMatchPatch, DiffOp } from 'diff-match-patch-ts';
7+
import React, { useEffect, useRef } from 'react';
8+
9+
import { useIsLightTheme } from '~/ui/hooks/theme';
10+
11+
// these global variables are required by codemirror merge addon
12+
window.diff_match_patch = DiffMatchPatch;
13+
window.DIFF_DELETE = DiffOp.Delete;
14+
window.DIFF_INSERT = DiffOp.Insert;
15+
window.DIFF_EQUAL = DiffOp.Equal;
16+
17+
interface Props {
18+
leftContent: string;
19+
rightContent: string;
20+
centerContent: string;
21+
onChange: (value: string) => void;
22+
}
23+
24+
export const MergeEditor = ({ leftContent, rightContent, centerContent, onChange }: Props) => {
25+
const divRef = useRef<HTMLDivElement>(null);
26+
const mergeViewRef = useRef<MergeView | null>(null);
27+
28+
const leftContentRef = useRef(leftContent);
29+
const rightContentRef = useRef(rightContent);
30+
const centerContentRef = useRef(centerContent);
31+
const onChangeRef = useRef<(value: string) => void>(onChange);
32+
33+
useEffect(() => {
34+
leftContentRef.current = leftContent;
35+
rightContentRef.current = rightContent;
36+
centerContentRef.current = centerContent;
37+
onChangeRef.current = onChange;
38+
}, [leftContent, rightContent, centerContent, onChange]);
39+
40+
const isLightTheme = useIsLightTheme();
41+
const isLightThemeRef = useRef(isLightTheme);
42+
43+
useEffect(() => {
44+
const onChange = (instance: CodeMirror.Editor) => {
45+
onChangeRef.current(instance.getDoc().getValue());
46+
};
47+
if (!divRef.current) {
48+
return;
49+
}
50+
const div = divRef.current;
51+
mergeViewRef.current = CodeMirror.MergeView(div, {
52+
value: centerContentRef.current,
53+
origLeft: leftContentRef.current,
54+
origRight: rightContentRef.current,
55+
lineNumbers: true,
56+
mode: 'yaml',
57+
theme: isLightThemeRef.current ? 'default' : 'base16-dark',
58+
});
59+
mergeViewRef.current.editor().on('changes', onChange);
60+
return () => {
61+
if (mergeViewRef.current) {
62+
mergeViewRef.current.editor().off('changes', onChange);
63+
}
64+
mergeViewRef.current = null;
65+
if (div) {
66+
div.innerHTML = '';
67+
}
68+
};
69+
}, []);
70+
71+
useEffect(() => {
72+
if (mergeViewRef.current?.editor().getDoc().getValue() !== centerContent) {
73+
mergeViewRef.current?.editor().getDoc().setValue(centerContent);
74+
}
75+
}, [centerContent]);
76+
77+
return (
78+
<div
79+
className={classnames('h-full', {
80+
'dark-merge-editor': !isLightTheme,
81+
})}
82+
ref={divRef}
83+
/>
84+
);
85+
};

packages/insomnia/src/ui/components/dropdowns/git-project-sync-dropdown.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from 'react-aria-components';
1515
import { useParams, useRevalidator } from 'react-router';
1616
import * as reactUse from 'react-use';
17+
import { parse, stringify } from 'yaml';
1718

1819
import { useGitProjectCheckoutBranchActionFetcher } from '~/routes/git.branch.checkout';
1920
import { useGitProjectFetchActionFetcher } from '~/routes/git.fetch';
@@ -35,6 +36,7 @@ import { GitPullRequiredModal } from '../modals/git-pull-required-modal';
3536
import { GitProjectRepositorySettingsModal } from '../modals/git-repository-settings-modal';
3637
import { SyncMergeModal } from '../modals/sync-merge-modal';
3738
import { showToast } from '../toast-notification';
39+
import resultSample from './merge-result-sample.json';
3840
interface Props {
3941
gitRepository?: GitRepository;
4042
}
@@ -477,6 +479,21 @@ export const GitProjectSyncDropdown: FC<Props> = ({ gitRepository }) => {
477479

478480
const pendingChangesCount = status?.localChanges ?? 0;
479481

482+
useEffect(() => {
483+
for (const conflict of resultSample.conflicts) {
484+
if (typeof conflict.mergeResult !== 'string') {
485+
conflict.mergeResult = stringify(conflict.mergeResult);
486+
}
487+
}
488+
showModal(SyncMergeModal, {
489+
editorType: 'merge',
490+
conflicts: resultSample.conflicts,
491+
labels: resultSample.labels,
492+
onResolveAll: (conflicts: MergeConflict[]) => {},
493+
onCancelUnresolved: () => {},
494+
});
495+
}, []);
496+
480497
return (
481498
<>
482499
{operationError && (

0 commit comments

Comments
 (0)