|
| 1 | +/*--------------------------------------------------------------------------------------------- |
| 2 | + * Copyright (c) Microsoft Corporation. All rights reserved. |
| 3 | + * Licensed under the MIT License. See License.txt in the project root for license information. |
| 4 | + *--------------------------------------------------------------------------------------------*/ |
| 5 | + |
| 6 | +'use strict'; |
| 7 | + |
| 8 | +import * as path from 'path'; |
| 9 | +import * as fs from 'fs'; |
| 10 | +import * as platform from 'vs/base/common/platform'; |
| 11 | +import * as paths from 'vs/base/common/paths'; |
| 12 | +import { OpenContext } from 'vs/platform/windows/common/windows'; |
| 13 | + |
| 14 | +/** |
| 15 | + * Exported subset of CodeWindow for testing. |
| 16 | + */ |
| 17 | +export interface ISimpleWindow { |
| 18 | + openedWorkspacePath: string; |
| 19 | + lastFocusTime: number; |
| 20 | +} |
| 21 | + |
| 22 | +/** |
| 23 | + * Exported for testing. |
| 24 | + */ |
| 25 | +export interface IBestWindowOrFolderOptions<W extends ISimpleWindow> { |
| 26 | + windows: W[]; |
| 27 | + newWindow: boolean; |
| 28 | + reuseWindow: boolean; |
| 29 | + context: OpenContext; |
| 30 | + filePath?: string; |
| 31 | + userHome?: string; |
| 32 | + codeSettingsFolder?: string; |
| 33 | +} |
| 34 | + |
| 35 | +export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, filePath, userHome, codeSettingsFolder }: IBestWindowOrFolderOptions<W>): W | string { |
| 36 | + if (!newWindow && filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) { |
| 37 | + const windowOnFilePath = findWindowOnFilePath(windows, filePath); |
| 38 | + const folderWithCodeSettings = !reuseWindow && findFolderWithCodeSettings(filePath, userHome, codeSettingsFolder); |
| 39 | + |
| 40 | + // Return if we found a window that has the parent of the file path opened |
| 41 | + if (windowOnFilePath && !(folderWithCodeSettings && folderWithCodeSettings.length > windowOnFilePath.openedWorkspacePath.length)) { |
| 42 | + return windowOnFilePath; |
| 43 | + } |
| 44 | + |
| 45 | + // Return if we found a parent folder with a code settings folder inside |
| 46 | + if (folderWithCodeSettings) { |
| 47 | + return folderWithCodeSettings; |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + return !newWindow ? getLastActiveWindow(windows) : null; |
| 52 | +} |
| 53 | + |
| 54 | +function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], filePath: string): W { |
| 55 | + |
| 56 | + // From all windows that have the parent of the file opened, return the window |
| 57 | + // that has the most specific folder opened ( = longest path wins) |
| 58 | + const windowsOnFilePath = windows.filter(window => typeof window.openedWorkspacePath === 'string' && paths.isEqualOrParent(filePath, window.openedWorkspacePath, !platform.isLinux /* ignorecase */)); |
| 59 | + if (windowsOnFilePath.length) { |
| 60 | + return windowsOnFilePath.sort((a, b) => -(a.openedWorkspacePath.length - b.openedWorkspacePath.length))[0]; |
| 61 | + } |
| 62 | + |
| 63 | + return null; |
| 64 | +} |
| 65 | + |
| 66 | +function findFolderWithCodeSettings(filePath: string, userHome?: string, codeSettingsFolder?: string): string { |
| 67 | + let folder = path.dirname(paths.normalize(filePath, true)); |
| 68 | + let homeFolder = userHome && paths.normalize(userHome, true); |
| 69 | + if (!platform.isLinux) { |
| 70 | + homeFolder = homeFolder && homeFolder.toLowerCase(); |
| 71 | + } |
| 72 | + |
| 73 | + let previous = null; |
| 74 | + while (folder !== previous) { |
| 75 | + if (hasCodeSettings(folder, homeFolder, codeSettingsFolder)) { |
| 76 | + return folder; |
| 77 | + } |
| 78 | + |
| 79 | + previous = folder; |
| 80 | + folder = path.dirname(folder); |
| 81 | + } |
| 82 | + |
| 83 | + return null; |
| 84 | +} |
| 85 | + |
| 86 | +function hasCodeSettings(folder: string, normalizedUserHome?: string, codeSettingsFolder = '.vscode') { |
| 87 | + try { |
| 88 | + if ((platform.isLinux ? folder : folder.toLowerCase()) === normalizedUserHome) { |
| 89 | + return fs.statSync(path.join(folder, codeSettingsFolder, 'settings.json')).isFile(); // ~/.vscode/extensions is used for extensions |
| 90 | + } |
| 91 | + |
| 92 | + return fs.statSync(path.join(folder, codeSettingsFolder)).isDirectory(); |
| 93 | + } catch (err) { |
| 94 | + // assume impossible to access |
| 95 | + } |
| 96 | + |
| 97 | + return false; |
| 98 | +} |
| 99 | + |
| 100 | +export function getLastActiveWindow<W extends ISimpleWindow>(windows: W[]): W { |
| 101 | + if (windows.length) { |
| 102 | + const lastFocussedDate = Math.max.apply(Math, windows.map(w => w.lastFocusTime)); |
| 103 | + const res = windows.filter(w => w.lastFocusTime === lastFocussedDate); |
| 104 | + if (res && res.length) { |
| 105 | + return res[0]; |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + return null; |
| 110 | +} |
0 commit comments