polish cli args parsing

This commit is contained in:
Martin Aeschlimann
2018-08-09 17:54:54 +02:00
parent b1f3ae287e
commit 8b8feed4ad
5 changed files with 112 additions and 73 deletions

View File

@@ -65,6 +65,7 @@ import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService
import { MenubarChannel } from 'vs/platform/menubar/common/menubarIpc';
import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay';
import { CodeMenu } from 'vs/code/electron-main/menus';
import { hasArgs } from 'vs/code/node/args';
export class CodeApplication {
@@ -467,16 +468,16 @@ export class CodeApplication {
// Open our first window
const macOpenFiles = (<any>global).macOpenFiles as string[];
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
const cliArgs = args._ || [];
const folderURIs = asArray(args['folder-uri']);
const fileURIs = asArray(args['file-uri']);
const hasCliArgs = hasArgs(args._);
const hasFolderURIs = hasArgs(args['folder-uri']);
const hasFileURIs = hasArgs(args['file-uri']);
if (args['new-window'] && !cliArgs.length && !folderURIs.length && !fileURIs.length) {
if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
} else if (macOpenFiles && macOpenFiles.length && !cliArgs.length && !folderURIs.length && !fileURIs.length) {
} else if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, urisToOpen: macOpenFiles.map(file => URI.file(file)), initialStartup: true }); // mac: open-file event received on startup
} else {
this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!cliArgs.length && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli
this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli
}
}

View File

@@ -20,7 +20,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import URI, { UriComponents } from 'vs/base/common/uri';
import { BrowserWindow } from 'electron';
import { Event } from 'vs/base/common/event';
import { asArray } from 'vs/code/node/args';
import { hasArgs } from 'vs/code/node/args';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
@@ -179,7 +179,7 @@ export class LaunchService implements ILaunchService {
}
// Start without file/folder arguments
else if (args._.length === 0 && !asArray(args['folder-uri'].length && !asArray(args['file-uri'].length))) {
else if (!hasArgs(args._) && !hasArgs(args['folder-uri']) && !hasArgs(args['file-uri'])) {
let openNewWindow = false;
// Force new window

View File

@@ -14,7 +14,7 @@ import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { IStateService } from 'vs/platform/state/common/state';
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
import { asArray } from 'vs/code/node/args';
import { asArray, hasArgs } from 'vs/code/node/args';
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, app } from 'electron';
import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/paths';
import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
@@ -423,7 +423,7 @@ export class WindowsManager implements IWindowsMainService {
// Make sure to pass focus to the most relevant of the windows if we open multiple
if (usedWindows.length > 1) {
let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !asArray(openConfig.cli['file-uri']).length && !asArray(openConfig.cli['folder-uri']).length && !asArray(openConfig.urisToOpen).length;
let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !hasArgs(openConfig.cli._) && !hasArgs(openConfig.cli['file-uri']) && !hasArgs(openConfig.cli['folder-uri']) && !hasArgs(openConfig.urisToOpen);
let focusLastOpened = true;
let focusLastWindow = true;
@@ -795,7 +795,7 @@ export class WindowsManager implements IWindowsMainService {
}
// Extract paths: from CLI
else if (openConfig.cli._.length > 0 || asArray(openConfig.cli['folder-uri']).length > 0 || asArray(openConfig.cli['file-uri']).length > 0) {
else if (hasArgs(openConfig.cli._) || hasArgs(openConfig.cli['folder-uri']) || hasArgs(openConfig.cli['file-uri'])) {
windowsToOpen = this.doExtractPathsFromCLI(openConfig.cli);
isCommandLineOrAPICall = true;
}
@@ -824,53 +824,74 @@ export class WindowsManager implements IWindowsMainService {
}
private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPath[] {
let pathsToOpen = openConfig.urisToOpen.map(pathToOpen => {
const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile, { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile });
let pathsToOpen = [];
let parseOptions = { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile };
for (const pathToOpen of openConfig.urisToOpen) {
if (!pathToOpen) {
continue;
}
// Warn if the requested path to open does not exist
if (!path) {
const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile, parseOptions);
if (path) {
pathsToOpen.push(path);
} else {
// Warn about the invalid URI or path
let message, detail;
if (pathToOpen.scheme === Schemas.file) {
message = localize('pathNotExistTitle', "Path does not exist");
detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen.fsPath);
} else {
message = localize('uriInvalidTitle', "URI can not be opened");
detail = localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", pathToOpen.toString());
}
const options: Electron.MessageBoxOptions = {
title: product.nameLong,
type: 'info',
buttons: [localize('ok', "OK")],
message: localize('pathNotExistTitle', "Path does not exist"),
detail: localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen.scheme === Schemas.file ? pathToOpen.fsPath : pathToOpen.path),
message,
detail,
noLink: true
};
this.dialogs.showMessageBox(options, this.getFocusedWindow());
}
return path;
});
// get rid of nulls
pathsToOpen = arrays.coalesce(pathsToOpen);
}
return pathsToOpen;
}
private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] {
const pathsToOpen = [];
const parseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto };
// folder uris
const folderUris = asArray(cli['folder-uri']);
if (folderUris.length) {
pathsToOpen.push(...arrays.coalesce(folderUris.map(candidate => this.parseUri(this.parseUriArg(candidate), false, { ignoreFileNotFound: true, gotoLineMode: cli.goto }))));
for (let folderUri of folderUris) {
const path = this.parseUri(this.argToUri(folderUri), false, parseOptions);
if (path) {
pathsToOpen.push(path);
}
}
// file uris
const fileUris = asArray(cli['file-uri']);
if (fileUris.length) {
pathsToOpen.push(...arrays.coalesce(fileUris.map(candidate => this.parseUri(this.parseUriArg(candidate), true, { ignoreFileNotFound: true, gotoLineMode: cli.goto }))));
for (let fileUri of fileUris) {
const path = this.parseUri(this.argToUri(fileUri), true, parseOptions);
if (path) {
pathsToOpen.push(path);
}
}
// folder or file paths
if (cli._ && cli._.length) {
pathsToOpen.push(...arrays.coalesce(cli._.map(candidate => this.parsePath(candidate, { ignoreFileNotFound: true, gotoLineMode: cli.goto }))));
const cliArgs = asArray(cli._);
for (let cliArg of cliArgs) {
const path = this.parsePath(cliArg, parseOptions);
if (path) {
pathsToOpen.push(path);
}
}
if (pathsToOpen.length > 0) {
if (pathsToOpen.length) {
return pathsToOpen;
}
@@ -974,29 +995,34 @@ export class WindowsManager implements IWindowsMainService {
return restoreWindows;
}
private parseUriArg(arg: string): URI {
// Do not support if user has passed folder path on Windows
if (isWindows && /^([a-z])\:(.*)$/i.test(arg)) {
return null;
private argToUri(arg: string): URI {
try {
let uri = URI.parse(arg);
if (!uri.scheme) {
console.log(`Invalid URI string, scheme missing: ${arg}`);
return null;
}
return uri;
} catch (e) {
console.log(`Invalid URI string, scheme missing: ${e.message}`);
}
return URI.parse(arg);
return null;
}
private parseUri(anyUri: URI, isFile: boolean, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen {
if (!anyUri || !anyUri.scheme) {
private parseUri(uri: URI, isFile: boolean, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen {
if (!uri || !uri.scheme) {
return null;
}
if (anyUri.scheme === Schemas.file) {
return this.parsePath(anyUri.fsPath, options);
if (uri.scheme === Schemas.file) {
return this.parsePath(uri.fsPath, options);
}
if (isFile) {
return {
fileUri: anyUri
fileUri: uri
};
}
return {
folderUri: anyUri
folderUri: uri
};
}
@@ -1135,11 +1161,11 @@ export class WindowsManager implements IWindowsMainService {
cliArgs = [];
}
if (folderUris.length && folderUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.parseUriArg(uri)))) {
if (folderUris.length && folderUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) {
folderUris = [];
}
if (fileUris.length && fileUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.parseUriArg(uri)))) {
if (fileUris.length && fileUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) {
fileUris = [];
}

View File

@@ -6,7 +6,7 @@
'use strict';
/**
* Converts an arument into to an array
* Converts an argument into an array
* @param arg a argument value. Can be undefined, en entry or an array
*/
export function asArray<T>(arg: T | T[] | undefined): T[] {
@@ -17,4 +17,17 @@ export function asArray<T>(arg: T | T[] | undefined): T[] {
return [arg];
}
return [];
}
/**
* Returns whether an argument is present.
*/
export function hasArgs<T>(arg: T | T[] | undefined): boolean {
if (arg) {
if (Array.isArray(arg)) {
return !!arg.length;
}
return true;
}
return false;
}

View File

@@ -8,7 +8,7 @@
import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
import { OpenContext } from 'vs/platform/windows/common/windows';
import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import URI from 'vs/base/common/uri';
import { hasToIgnoreCase, isEqual, isEqualOrParent } from 'vs/base/common/resources';
@@ -70,51 +70,50 @@ export function getLastActiveWindow<W extends ISimpleWindow>(windows: W[]): W {
}
export function findWindowOnWorkspace<W extends ISimpleWindow>(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W {
return windows.filter(window => {
// match on folder
if (isSingleFolderWorkspaceIdentifier(workspace)) {
if (window.openedFolderUri && isEqual(window.openedFolderUri, workspace, hasToIgnoreCase(window.openedFolderUri))) {
return true;
if (isSingleFolderWorkspaceIdentifier(workspace)) {
for (const window of windows) {
// match on folder
if (isSingleFolderWorkspaceIdentifier(workspace)) {
if (window.openedFolderUri && isEqual(window.openedFolderUri, workspace, hasToIgnoreCase(window.openedFolderUri))) {
return window;
}
}
}
// match on workspace
else {
} else if (isWorkspaceIdentifier(workspace)) {
for (const window of windows) {
// match on workspace
if (window.openedWorkspace && window.openedWorkspace.id === workspace.id) {
return true;
return window;
}
}
return false;
})[0];
}
return null;
}
export function findWindowOnExtensionDevelopmentPath<W extends ISimpleWindow>(windows: W[], extensionDevelopmentPath: string): W {
return windows.filter(window => {
for (const window of windows) {
// match on extension development path
if (paths.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) {
return true;
return window;
}
return false;
})[0];
}
return null;
}
export function findWindowOnWorkspaceOrFolderUri<W extends ISimpleWindow>(windows: W[], uri: URI): W {
return windows.filter(window => {
if (!uri) {
return null;
}
for (const window of windows) {
// check for workspace config path
if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), uri, !platform.isLinux /* ignorecase */)) {
return true;
return window;
}
// check for folder path
if (window.openedFolderUri && isEqual(window.openedFolderUri, uri, hasToIgnoreCase(uri))) {
return true;
return window;
}
return false;
})[0];
}
return null;
}