Skip to content

Commit a567b59

Browse files
authored
introduce --merge to bring up merge editor (for microsoft#5770) (microsoft#155039)
* introduce `--merge` to bring up merge editor (for microsoft#5770) * wait on proper editor when merging * sqlite slowness * disable flush on write in tests unless disk tests * more runWithFakedTimers * disable flush also in pfs * introduce `IResourceMergeEditorInput` * cleanup * align with merge editor names * stronger check * adopt `ResourceSet` * no need to coalesce * improve `matches` method
1 parent 03c16c9 commit a567b59

File tree

33 files changed

+494
-197
lines changed

33 files changed

+494
-197
lines changed

src/vs/code/electron-main/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@ export class CodeApplication extends Disposable {
10121012
cli: args,
10131013
forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']),
10141014
diffMode: args.diff,
1015+
mergeMode: args.merge,
10151016
noRecentEntry,
10161017
waitMarkerFileURI,
10171018
gotoLineMode: args.goto,

src/vs/platform/environment/common/argv.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface NativeParsedArgs {
1818
wait?: boolean;
1919
waitMarkerFilePath?: string;
2020
diff?: boolean;
21+
merge?: boolean;
2122
add?: boolean;
2223
goto?: boolean;
2324
'new-window'?: boolean;

src/vs/platform/environment/node/argv.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type OptionTypeName<T> =
4141

4242
export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
4343
'diff': { type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") },
44+
'merge': { type: 'boolean', cat: 'o', alias: 'm', args: ['path1', 'path2', 'base', 'result'], description: localize('merge', "Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results.") },
4445
'add': { type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") },
4546
'goto': { type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") },
4647
'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") },

src/vs/platform/launch/electron-main/launchMainService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ export class LaunchMainService implements ILaunchMainService {
190190
preferNewWindow: !args['reuse-window'] && !args.wait,
191191
forceReuseWindow: args['reuse-window'],
192192
diffMode: args.diff,
193+
mergeMode: args.merge,
193194
addMode: args.add,
194195
noRecentEntry: !!args['skip-add-to-recently-opened'],
195196
gotoLineMode: args.goto

src/vs/platform/native/electron-main/nativeHostMainService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
154154
forceReuseWindow: options.forceReuseWindow,
155155
preferNewWindow: options.preferNewWindow,
156156
diffMode: options.diffMode,
157+
mergeMode: options.mergeMode,
157158
addMode: options.addMode,
158159
gotoLineMode: options.gotoLineMode,
159160
noRecentEntry: options.noRecentEntry,

src/vs/platform/window/common/window.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export interface IOpenWindowOptions extends IBaseOpenWindowsOptions {
5252
readonly addMode?: boolean;
5353

5454
readonly diffMode?: boolean;
55+
readonly mergeMode?: boolean;
5556
readonly gotoLineMode?: boolean;
5657

5758
readonly waitMarkerFileURI?: URI;
@@ -240,6 +241,7 @@ interface IPathsToWaitForData {
240241
export interface IOpenFileRequest {
241242
readonly filesToOpenOrCreate?: IPathData[];
242243
readonly filesToDiff?: IPathData[];
244+
readonly filesToMerge?: IPathData[];
243245
}
244246

245247
/**
@@ -270,6 +272,7 @@ export interface IWindowConfiguration {
270272

271273
filesToOpenOrCreate?: IPath[];
272274
filesToDiff?: IPath[];
275+
filesToMerge?: IPath[];
273276
}
274277

275278
export interface IOSConfiguration {

src/vs/platform/windows/electron-main/window.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
899899
// Delete some properties we do not want during reload
900900
delete configuration.filesToOpenOrCreate;
901901
delete configuration.filesToDiff;
902+
delete configuration.filesToMerge;
902903
delete configuration.filesToWait;
903904

904905
// Some configuration things get inherited if the window is being reloaded and we are

src/vs/platform/windows/electron-main/windows.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export interface IOpenConfiguration extends IBaseOpenConfiguration {
8585
readonly forceReuseWindow?: boolean;
8686
readonly forceEmpty?: boolean;
8787
readonly diffMode?: boolean;
88+
readonly mergeMode?: boolean;
8889
addMode?: boolean;
8990
readonly gotoLineMode?: boolean;
9091
readonly initialStartup?: boolean;

src/vs/platform/windows/electron-main/windowsMainService.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ interface IFilesToOpen {
118118

119119
filesToOpenOrCreate: IPath[];
120120
filesToDiff: IPath[];
121+
filesToMerge: IPath[];
122+
121123
filesToWait?: IPathsToWaitFor;
122124
}
123125

@@ -296,7 +298,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
296298
workspacesToOpen.push(path);
297299
} else if (path.fileUri) {
298300
if (!filesToOpen) {
299-
filesToOpen = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority };
301+
filesToOpen = { filesToOpenOrCreate: [], filesToDiff: [], filesToMerge: [], remoteAuthority: path.remoteAuthority };
300302
}
301303
filesToOpen.filesToOpenOrCreate.push(path);
302304
} else if (path.backupPath) {
@@ -312,9 +314,16 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
312314
filesToOpen.filesToOpenOrCreate = [];
313315
}
314316

317+
// When run with --merge, take the first 4 files to open as files to merge
318+
if (openConfig.mergeMode && filesToOpen && filesToOpen.filesToOpenOrCreate.length === 4) {
319+
filesToOpen.filesToMerge = filesToOpen.filesToOpenOrCreate.slice(0, 4);
320+
filesToOpen.filesToOpenOrCreate = [];
321+
filesToOpen.filesToDiff = [];
322+
}
323+
315324
// When run with --wait, make sure we keep the paths to wait for
316325
if (filesToOpen && openConfig.waitMarkerFileURI) {
317-
filesToOpen.filesToWait = { paths: [...filesToOpen.filesToDiff, ...filesToOpen.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI };
326+
filesToOpen.filesToWait = { paths: [...filesToOpen.filesToDiff, filesToOpen.filesToMerge[3] /* [3] is the resulting merge file */, ...filesToOpen.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI };
318327
}
319328

320329
// These are windows to restore because of hot-exit or from previous session (only performed once on startup!)
@@ -384,9 +393,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
384393
}
385394

386395
// Remember in recent document list (unless this opens for extension development)
387-
// Also do not add paths when files are opened for diffing, only if opened individually
396+
// Also do not add paths when files are opened for diffing or merging, only if opened individually
388397
const isDiff = filesToOpen && filesToOpen.filesToDiff.length > 0;
389-
if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !isDiff && !openConfig.noRecentEntry) {
398+
const isMerge = filesToOpen && filesToOpen.filesToMerge.length > 0;
399+
if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !isDiff && !isMerge && !openConfig.noRecentEntry) {
390400
const recents: IRecent[] = [];
391401
for (const pathToOpen of pathsToOpen) {
392402
if (isWorkspacePathToOpen(pathToOpen) && !pathToOpen.transient /* never add transient workspaces to history */) {
@@ -461,13 +471,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
461471
}
462472
}
463473

464-
// Handle files to open/diff or to create when we dont open a folder and we do not restore any
474+
// Handle files to open/diff/merge or to create when we dont open a folder and we do not restore any
465475
// folder/untitled from hot-exit by trying to open them in the window that fits best
466476
const potentialNewWindowsCount = foldersToOpen.length + workspacesToOpen.length + emptyToRestore.length;
467477
if (filesToOpen && potentialNewWindowsCount === 0) {
468478

469479
// Find suitable window or folder path to open files in
470-
const fileToCheck = filesToOpen.filesToOpenOrCreate[0] || filesToOpen.filesToDiff[0];
480+
const fileToCheck = filesToOpen.filesToOpenOrCreate[0] || filesToOpen.filesToDiff[0] || filesToOpen.filesToMerge[3] /* [3] is the resulting merge file */;
471481

472482
// only look at the windows with correct authority
473483
const windows = this.getWindows().filter(window => filesToOpen && isEqualAuthority(window.remoteAuthority, filesToOpen.remoteAuthority));
@@ -625,6 +635,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
625635
const params: INativeOpenFileRequest = {
626636
filesToOpenOrCreate: filesToOpen?.filesToOpenOrCreate,
627637
filesToDiff: filesToOpen?.filesToDiff,
638+
filesToMerge: filesToOpen?.filesToMerge,
628639
filesToWait: filesToOpen?.filesToWait,
629640
termProgram: configuration?.userEnv?.['TERM_PROGRAM']
630641
};
@@ -792,7 +803,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
792803
ignoreFileNotFound: true,
793804
gotoLineMode: cli.goto,
794805
remoteAuthority: cli.remote || undefined,
795-
forceOpenWorkspaceAsFile: cli.diff && cli._.length === 2 // special case diff mode to force open workspace as file (https://github.com/microsoft/vscode/issues/149731)
806+
forceOpenWorkspaceAsFile:
807+
// special case diff / merge mode to force open
808+
// workspace as file
809+
// https://github.com/microsoft/vscode/issues/149731
810+
cli.diff && cli._.length === 2 ||
811+
cli.merge && cli._.length === 4
796812
};
797813

798814
// folder uris
@@ -1323,6 +1339,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
13231339

13241340
filesToOpenOrCreate: options.filesToOpen?.filesToOpenOrCreate,
13251341
filesToDiff: options.filesToOpen?.filesToDiff,
1342+
filesToMerge: options.filesToOpen?.filesToMerge,
13261343
filesToWait: options.filesToOpen?.filesToWait,
13271344

13281345
logLevel: this.logService.getLevel(),

src/vs/server/node/server.cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ const isSupportedForPipe = (optionId: keyof RemoteParsedArgs) => {
5959
case 'file-uri':
6060
case 'add':
6161
case 'diff':
62+
case 'merge':
6263
case 'wait':
6364
case 'goto':
6465
case 'reuse-window':
@@ -297,6 +298,7 @@ export function main(desc: ProductDescription, args: string[]): void {
297298
fileURIs,
298299
folderURIs,
299300
diffMode: parsedArgs.diff,
301+
mergeMode: parsedArgs.merge,
300302
addMode: parsedArgs.add,
301303
gotoLineMode: parsedArgs.goto,
302304
forceReuseWindow: parsedArgs['reuse-window'],

0 commit comments

Comments
 (0)