Flow.Launcher/Flow.Launcher.Core/ExternalPlugins/PluginEnvironment.cs

229 lines
8.8 KiB
C#

using Droplex;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace Flow.Launcher.Core.ExternalPlugins
{
public class PluginEnvironment
{
private const string PythonExecutable = "pythonw.exe";
private const string NodeExecutable = "node.exe";
private const string PythonEnv = "Python";
private const string NodeEnv = "Node.js";
private List<PluginMetadata> pluginMetadataList;
private PluginsSettings pluginSettings;
internal PluginEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings)
{
this.pluginMetadataList = pluginMetadataList;
this.pluginSettings = pluginSettings;
}
//TODO: CHECK IF NEED TO RESET PATH AFTER FLOW UPDATE
// LOG NODE PATH
internal IEnumerable<PluginPair> PythonSetup()
{
return Setup(AllowedLanguage.Python, PythonEnv);
}
internal IEnumerable<PluginPair> TypeScriptSetup()
{
return Setup(AllowedLanguage.TypeScript, NodeEnv);
}
internal IEnumerable<PluginPair> JavaScriptSetup()
{
return Setup(AllowedLanguage.JavaScript, NodeEnv);
}
private IEnumerable<PluginPair> Setup(string languageType, string environment)
{
if (!pluginMetadataList.Any(o => o.Language.Equals(languageType, StringComparison.OrdinalIgnoreCase)))
return new List<PluginPair>();
var envFilePath = string.Empty;
switch (languageType)
{
case AllowedLanguage.Python:
if (!string.IsNullOrEmpty(pluginSettings.PythonFilePath) && FilesFolders.FileExists(pluginSettings.PythonFilePath))
return SetPathForPluginPairs($"{pluginSettings.PythonFilePath}\\{PythonExecutable}", languageType);
break;
case AllowedLanguage.TypeScript:
case AllowedLanguage.JavaScript:
if (!string.IsNullOrEmpty(pluginSettings.NodeFilePath) && FilesFolders.FileExists(pluginSettings.NodeFilePath))
return SetPathForPluginPairs(pluginSettings.NodeFilePath, languageType);
break;
default:
break;
}
if (MessageBox.Show($"Flow detected you have installed {languageType} plugins, which " +
$"will require {environment} to run. Would you like to download {environment}? " +
Environment.NewLine + Environment.NewLine +
"Click no if it's already installed, " +
$"and you will be prompted to select the folder that contains the {environment} executable",
string.Empty, MessageBoxButtons.YesNo) == DialogResult.No)
{
var msg = $"Please select the {environment} executable";
var selectedFile = string.Empty;
switch (languageType)
{
case AllowedLanguage.Python:
selectedFile = GetFileFromDialog(msg, "Python|pythonw.exe");
if (!string.IsNullOrEmpty(selectedFile))
pluginSettings.PythonFilePath = selectedFile;
break;
case AllowedLanguage.TypeScript:
case AllowedLanguage.JavaScript:
selectedFile = GetFileFromDialog(msg);
if (!string.IsNullOrEmpty(selectedFile))
pluginSettings.NodeFilePath = selectedFile;
break;
default:
break;
}
// Nothing selected because user pressed cancel from the file dialog window
if (string.IsNullOrEmpty(selectedFile))
InstallEnvironment(languageType);
}
else
{
InstallEnvironment(languageType);
}
switch (languageType)
{
case AllowedLanguage.Python when FilesFolders.FileExists(pluginSettings.PythonFilePath):
return SetPathForPluginPairs(pluginSettings.PythonFilePath, languageType);
case AllowedLanguage.TypeScript when FilesFolders.FileExists(pluginSettings.NodeFilePath):
case AllowedLanguage.JavaScript when FilesFolders.FileExists(pluginSettings.NodeFilePath):
return SetPathForPluginPairs(pluginSettings.NodeFilePath, languageType);
default:
MessageBox.Show(
"Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom).");
Log.Error("PluginsLoader",
$"Not able to successfully set Python path, setting's PythonFilePath variable is still an empty string.",
"PluginEnvironment");
return new List<PluginPair>();
}
}
private void InstallEnvironment(string languageType)
{
switch (languageType)
{//TODO: UPDATE TO USE CENTRALISED PLUGINENVIRONMENT FOLDER
case AllowedLanguage.Python:
var pythonDirPath = Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable");
FilesFolders.RemoveFolderIfExists(pythonDirPath);
// Python 3.8.9 is used for Windows 7 compatibility
DroplexPackage.Drop(App.python_3_8_9_embeddable, pythonDirPath).Wait();
pluginSettings.PythonFilePath = Path.Combine(pythonDirPath, PythonExecutable);
break;
case AllowedLanguage.TypeScript:
case AllowedLanguage.JavaScript:
var nodeDirPath = Path.Combine(DataLocation.DataDirectory(), "Node");
FilesFolders.RemoveFolderIfExists(nodeDirPath);
DroplexPackage.Drop(App.nodejs_16_18_0, nodeDirPath).Wait();
pluginSettings.NodeFilePath = Path.Combine(nodeDirPath, $"node-v16.18.0-win-x64\\{NodeExecutable}");
break;
default:
break;
}
}
private IEnumerable<PluginPair> SetPathForPluginPairs(string filePath, string languageToSet)
{
var pluginPairs = new List<PluginPair>();
foreach (var metadata in pluginMetadataList) {
switch (languageToSet)
{
case AllowedLanguage.Python when metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase):
pluginPairs.Add(new PluginPair
{
Plugin = new PythonPlugin(filePath),
Metadata = metadata
});
break;
case AllowedLanguage.TypeScript when metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase):
pluginPairs.Add(new PluginPair
{
Plugin = new NodePlugin(filePath),
Metadata = metadata
});
break;
case AllowedLanguage.JavaScript when metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase):
pluginPairs.Add(new PluginPair
{
Plugin = new NodePlugin(filePath),
Metadata = metadata
});
break;
default:
break;
}
}
return pluginPairs;
}
public static string GetFileFromDialog(string title, string filter="")
{
var dlg = new OpenFileDialog
{
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
Multiselect = false,
CheckFileExists = true,
CheckPathExists = true,
Title = title,
Filter = filter
};
var result = dlg.ShowDialog();
if (result == DialogResult.OK)
{
return dlg.FileName;
}
else
{
return string.Empty;
}
}
}
}