This commit is contained in:
Benjamin Pasero
2017-07-10 09:46:47 +02:00
parent 82ebb10872
commit dd06f3ca21
4 changed files with 144 additions and 140 deletions
+9 -8
View File
@@ -21,7 +21,7 @@ import { ILifecycleService, UnloadReason } from 'vs/platform/lifecycle/electron-
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log'; import { ILogService } from 'vs/platform/log/common/log';
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWindowSettings, OpenContext, IPath, IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { getLastActiveWindow, findBestWindowOrFolder } from 'vs/code/node/windowsUtils'; import { getLastActiveWindow, findBestWindowOrFolderForFile } from 'vs/code/node/windowsFinder';
import CommonEvent, { Emitter } from 'vs/base/common/event'; import CommonEvent, { Emitter } from 'vs/base/common/event';
import product from 'vs/platform/node/product'; import product from 'vs/platform/node/product';
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
@@ -345,7 +345,7 @@ export class WindowsManager implements IWindowsMainService {
// Open Files in last instance if any and flag tells us so // Open Files in last instance if any and flag tells us so
const fileToCheck = filesToOpen[0] || filesToCreate[0] || filesToDiff[0]; const fileToCheck = filesToOpen[0] || filesToCreate[0] || filesToDiff[0];
const windowOrFolder = findBestWindowOrFolder({ const bestWindowOrFolder = findBestWindowOrFolderForFile({
windows: WindowsManager.WINDOWS, windows: WindowsManager.WINDOWS,
newWindow: openFilesInNewWindow, newWindow: openFilesInNewWindow,
reuseWindow: openConfig.forceReuseWindow, reuseWindow: openConfig.forceReuseWindow,
@@ -354,23 +354,24 @@ export class WindowsManager implements IWindowsMainService {
userHome: this.environmentService.userHome userHome: this.environmentService.userHome
}); });
if (windowOrFolder instanceof CodeWindow) { // We found a suitable window to open the files within
windowOrFolder.focus(); if (bestWindowOrFolder instanceof CodeWindow) {
bestWindowOrFolder.focus();
const files = { filesToOpen, filesToCreate, filesToDiff }; // copy to object because they get reset shortly after const files = { filesToOpen, filesToCreate, filesToDiff }; // copy to object because they get reset shortly after
windowOrFolder.ready().then(readyWindow => { bestWindowOrFolder.ready().then(readyWindow => {
readyWindow.send('vscode:openFiles', files); readyWindow.send('vscode:openFiles', files);
}); });
usedWindows.push(windowOrFolder); usedWindows.push(bestWindowOrFolder);
} }
// Otherwise open instance with files // Otherwise open a new window with the best folder to use for the file
else { else {
const browserWindow = this.openInBrowserWindow({ const browserWindow = this.openInBrowserWindow({
userEnv: openConfig.userEnv, userEnv: openConfig.userEnv,
cli: openConfig.cli, cli: openConfig.cli,
initialStartup: openConfig.initialStartup, initialStartup: openConfig.initialStartup,
workspacePath: windowOrFolder, workspacePath: bestWindowOrFolder,
filesToOpen, filesToOpen,
filesToCreate, filesToCreate,
filesToDiff, filesToDiff,
+110
View File
@@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import * as fs from 'fs';
import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
import { OpenContext } from 'vs/platform/windows/common/windows';
/**
* Exported subset of CodeWindow for testing.
*/
export interface ISimpleWindow {
openedWorkspacePath: string;
lastFocusTime: number;
}
/**
* Exported for testing.
*/
export interface IBestWindowOrFolderOptions<W extends ISimpleWindow> {
windows: W[];
newWindow: boolean;
reuseWindow: boolean;
context: OpenContext;
filePath?: string;
userHome?: string;
codeSettingsFolder?: string;
}
export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, filePath, userHome, codeSettingsFolder }: IBestWindowOrFolderOptions<W>): W | string {
if (!newWindow && filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) {
const windowOnFilePath = findWindowOnFilePath(windows, filePath);
const folderWithCodeSettings = !reuseWindow && findFolderWithCodeSettings(filePath, userHome, codeSettingsFolder);
// Return if we found a window that has the parent of the file path opened
if (windowOnFilePath && !(folderWithCodeSettings && folderWithCodeSettings.length > windowOnFilePath.openedWorkspacePath.length)) {
return windowOnFilePath;
}
// Return if we found a parent folder with a code settings folder inside
if (folderWithCodeSettings) {
return folderWithCodeSettings;
}
}
return !newWindow ? getLastActiveWindow(windows) : null;
}
function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], filePath: string): W {
// From all windows that have the parent of the file opened, return the window
// that has the most specific folder opened ( = longest path wins)
const windowsOnFilePath = windows.filter(window => typeof window.openedWorkspacePath === 'string' && paths.isEqualOrParent(filePath, window.openedWorkspacePath, !platform.isLinux /* ignorecase */));
if (windowsOnFilePath.length) {
return windowsOnFilePath.sort((a, b) => -(a.openedWorkspacePath.length - b.openedWorkspacePath.length))[0];
}
return null;
}
function findFolderWithCodeSettings(filePath: string, userHome?: string, codeSettingsFolder?: string): string {
let folder = path.dirname(paths.normalize(filePath, true));
let homeFolder = userHome && paths.normalize(userHome, true);
if (!platform.isLinux) {
homeFolder = homeFolder && homeFolder.toLowerCase();
}
let previous = null;
while (folder !== previous) {
if (hasCodeSettings(folder, homeFolder, codeSettingsFolder)) {
return folder;
}
previous = folder;
folder = path.dirname(folder);
}
return null;
}
function hasCodeSettings(folder: string, normalizedUserHome?: string, codeSettingsFolder = '.vscode') {
try {
if ((platform.isLinux ? folder : folder.toLowerCase()) === normalizedUserHome) {
return fs.statSync(path.join(folder, codeSettingsFolder, 'settings.json')).isFile(); // ~/.vscode/extensions is used for extensions
}
return fs.statSync(path.join(folder, codeSettingsFolder)).isDirectory();
} catch (err) {
// assume impossible to access
}
return false;
}
export function getLastActiveWindow<W extends ISimpleWindow>(windows: W[]): W {
if (windows.length) {
const lastFocussedDate = Math.max.apply(Math, windows.map(w => w.lastFocusTime));
const res = windows.filter(w => w.lastFocusTime === lastFocussedDate);
if (res && res.length) {
return res[0];
}
}
return null;
}
-107
View File
@@ -1,107 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import * as fs from 'fs';
import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
import { OpenContext } from 'vs/platform/windows/common/windows';
/**
* Exported subset of CodeWindow for testing.
*/
export interface ISimpleWindow {
openedWorkspacePath: string;
lastFocusTime: number;
}
/**
* Exported for testing.
*/
export interface IBestWindowOrFolderOptions<SimpleWindow extends ISimpleWindow> {
windows: SimpleWindow[];
newWindow: boolean;
reuseWindow: boolean;
context: OpenContext;
filePath?: string;
userHome?: string;
vscodeFolder?: string;
}
export function findBestWindowOrFolder<SimpleWindow extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, filePath, userHome, vscodeFolder }: IBestWindowOrFolderOptions<SimpleWindow>): SimpleWindow | string {
// OpenContext.DOCK implies newWindow unless overwritten by settings.
const findBest = filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK);
const bestWindow = !newWindow && findBest && findBestWindow(windows, filePath);
const bestFolder = !newWindow && !reuseWindow && findBest && findBestFolder(filePath, userHome, vscodeFolder);
if (bestWindow && !(bestFolder && bestFolder.length > bestWindow.openedWorkspacePath.length)) {
return bestWindow;
} else if (bestFolder) {
return bestFolder;
}
return !newWindow ? getLastActiveWindow(windows) : null;
}
function findBestWindow<WINDOW extends ISimpleWindow>(windows: WINDOW[], filePath: string): WINDOW {
const containers = windows.filter(window => typeof window.openedWorkspacePath === 'string' && paths.isEqualOrParent(filePath, window.openedWorkspacePath, !platform.isLinux /* ignorecase */));
if (containers.length) {
return containers.sort((a, b) => -(a.openedWorkspacePath.length - b.openedWorkspacePath.length))[0];
}
return null;
}
function findBestFolder(filePath: string, userHome?: string, vscodeFolder?: string): string {
let folder = path.dirname(paths.normalize(filePath, true));
let homeFolder = userHome && paths.normalize(userHome, true);
if (!platform.isLinux) {
homeFolder = homeFolder && homeFolder.toLowerCase();
}
let previous = null;
try {
while (folder !== previous) {
if (isProjectFolder(folder, homeFolder, vscodeFolder)) {
return folder;
}
previous = folder;
folder = path.dirname(folder);
}
} catch (err) {
// assume impossible to access
}
return null;
}
function isProjectFolder(folder: string, normalizedUserHome?: string, vscodeFolder = '.vscode') {
try {
if ((platform.isLinux ? folder : folder.toLowerCase()) === normalizedUserHome) {
return fs.statSync(path.join(folder, vscodeFolder, 'settings.json')).isFile(); // ~/.vscode/extensions is used for extensions
}
return fs.statSync(path.join(folder, vscodeFolder)).isDirectory();
} catch (err) {
if (!(err && err.code === 'ENOENT')) {
throw err;
}
}
return false;
}
export function getLastActiveWindow<WINDOW extends ISimpleWindow>(windows: WINDOW[]): WINDOW {
if (windows.length) {
const lastFocussedDate = Math.max.apply(Math, windows.map(w => w.lastFocusTime));
const res = windows.filter(w => w.lastFocusTime === lastFocussedDate);
if (res && res.length) {
return res[0];
}
}
return null;
}
+25 -25
View File
@@ -6,7 +6,7 @@
import assert = require('assert'); import assert = require('assert');
import path = require('path'); import path = require('path');
import { findBestWindowOrFolder, ISimpleWindow, IBestWindowOrFolderOptions } from 'vs/code/node/windowsUtils'; import { findBestWindowOrFolderForFile, ISimpleWindow, IBestWindowOrFolderOptions } from 'vs/code/node/windowsFinder';
import { OpenContext } from 'vs/platform/windows/common/windows'; import { OpenContext } from 'vs/platform/windows/common/windows';
const fixturesFolder = require.toUrl('./fixtures'); const fixturesFolder = require.toUrl('./fixtures');
@@ -17,7 +17,7 @@ function options(custom?: Partial<IBestWindowOrFolderOptions<ISimpleWindow>>): I
newWindow: false, newWindow: false,
reuseWindow: false, reuseWindow: false,
context: OpenContext.CLI, context: OpenContext.CLI,
vscodeFolder: '_vscode', codeSettingsFolder: '_vscode',
...custom ...custom
}; };
} }
@@ -31,38 +31,38 @@ const windows = [
noVscodeFolderWindow, noVscodeFolderWindow,
]; ];
suite('WindowsUtils', () => { suite('WindowsFinder', () => {
test('New window without folder when no windows exist', () => { test('New window without folder when no windows exist', () => {
assert.equal(findBestWindowOrFolder(options()), null); assert.equal(findBestWindowOrFolderForFile(options()), null);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt') filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')
})), null); })), null);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
newWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later. newWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later.
})), null); })), null);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
reuseWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later. reuseWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later.
})), null); })), null);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
context: OpenContext.API context: OpenContext.API
})), null); })), null);
}); });
test('New window with folder when no windows exist', () => { test('New window with folder when no windows exist', () => {
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder')); })), path.join(fixturesFolder, 'vscode_folder'));
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt')
})), path.join(fixturesFolder, 'vscode_folder')); })), path.join(fixturesFolder, 'vscode_folder'));
}); });
test('New window without folder when windows exist', () => { test('New window without folder when windows exist', () => {
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows, windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'),
newWindow: true newWindow: true
@@ -70,19 +70,19 @@ suite('WindowsUtils', () => {
}); });
test('Last active window', () => { test('Last active window', () => {
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows windows
})), lastActiveWindow); })), lastActiveWindow);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows, windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt') filePath: path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt')
})), lastActiveWindow); })), lastActiveWindow);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows: [lastActiveWindow, noVscodeFolderWindow], windows: [lastActiveWindow, noVscodeFolderWindow],
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
reuseWindow: true reuseWindow: true
})), lastActiveWindow); })), lastActiveWindow);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows, windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'),
context: OpenContext.API context: OpenContext.API
@@ -90,11 +90,11 @@ suite('WindowsUtils', () => {
}); });
test('Existing window with folder', () => { test('Existing window with folder', () => {
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows, windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt') filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')
})), noVscodeFolderWindow); })), noVscodeFolderWindow);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows, windows,
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
})), vscodeFolderWindow); })), vscodeFolderWindow);
@@ -102,12 +102,12 @@ suite('WindowsUtils', () => {
test('Existing window wins over vscode folder if more specific', () => { test('Existing window wins over vscode folder if more specific', () => {
const window = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder') }; const window = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder') };
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows: [window], windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), window); })), window);
// check // check
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows: [window], windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder2', 'subfolder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder2', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder')); })), path.join(fixturesFolder, 'vscode_folder'));
@@ -116,7 +116,7 @@ suite('WindowsUtils', () => {
test('More specific existing window wins', () => { test('More specific existing window wins', () => {
const window = { lastFocusTime: 2, openedWorkspacePath: path.join(fixturesFolder, 'no_vscode_folder') }; const window = { lastFocusTime: 2, openedWorkspacePath: path.join(fixturesFolder, 'no_vscode_folder') };
const nestedFolderWindow = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder') }; const nestedFolderWindow = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder') };
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows: [window, nestedFolderWindow], windows: [window, nestedFolderWindow],
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt') filePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), nestedFolderWindow); })), nestedFolderWindow);
@@ -124,30 +124,30 @@ suite('WindowsUtils', () => {
test('VSCode folder wins over existing window if more specific', () => { test('VSCode folder wins over existing window if more specific', () => {
const window = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'vscode_folder') }; const window = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'vscode_folder') };
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows: [window], windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder')); })), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder'));
// check // check
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows: [window], windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), window); })), window);
}); });
test('More specific VSCode folder wins', () => { test('More specific VSCode folder wins', () => {
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder')); })), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder'));
}); });
test('VSCode folder in home folder needs settings.json', () => { test('VSCode folder in home folder needs settings.json', () => {
// Because ~/.vscode/extensions is used for extensions, ~/.vscode is not enough as a hint. // Because ~/.vscode/extensions is used for extensions, ~/.vscode is not enough as a hint.
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
userHome: path.join(fixturesFolder, 'vscode_folder') userHome: path.join(fixturesFolder, 'vscode_folder')
})), null); })), null);
assert.equal(findBestWindowOrFolder(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_home_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'vscode_home_folder', 'file.txt'),
userHome: path.join(fixturesFolder, 'vscode_home_folder') userHome: path.join(fixturesFolder, 'vscode_home_folder')
})), path.join(fixturesFolder, 'vscode_home_folder')); })), path.join(fixturesFolder, 'vscode_home_folder'));