Skip to content

Commit ee662ab

Browse files
authored
Merge pull request #3407 from Jack251970/environment_issue
Fix JsonRPC Plugins Environment Issue & Improve Environment Selection
2 parents 0e4e95a + f9c1b6a commit ee662ab

File tree

8 files changed

+106
-50
lines changed

8 files changed

+106
-50
lines changed

Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
using Flow.Launcher.Infrastructure.Logger;
2-
using Flow.Launcher.Infrastructure.UserSettings;
3-
using Flow.Launcher.Plugin;
4-
using Flow.Launcher.Plugin.SharedCommands;
5-
using System;
1+
using System;
62
using System.Collections.Generic;
73
using System.IO;
84
using System.Linq;
95
using System.Windows;
106
using System.Windows.Forms;
11-
using Flow.Launcher.Core.Resource;
127
using CommunityToolkit.Mvvm.DependencyInjection;
8+
using Flow.Launcher.Infrastructure.Logger;
9+
using Flow.Launcher.Infrastructure.UserSettings;
10+
using Flow.Launcher.Plugin;
11+
using Flow.Launcher.Plugin.SharedCommands;
1312

1413
namespace Flow.Launcher.Core.ExternalPlugins.Environments
1514
{
@@ -43,8 +42,11 @@ internal AbstractPluginEnvironment(List<PluginMetadata> pluginMetadataList, Plug
4342

4443
internal IEnumerable<PluginPair> Setup()
4544
{
45+
// If no plugin is using the language, return empty list
4646
if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase)))
47+
{
4748
return new List<PluginPair>();
49+
}
4850

4951
if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath))
5052
{
@@ -56,24 +58,55 @@ internal IEnumerable<PluginPair> Setup()
5658
}
5759

5860
var noRuntimeMessage = string.Format(
59-
InternationalizationManager.Instance.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"),
61+
API.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"),
6062
Language,
6163
EnvName,
6264
Environment.NewLine
6365
);
6466
if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
6567
{
66-
var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
67-
string selectedFile;
68+
var msg = string.Format(API.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
6869

69-
selectedFile = GetFileFromDialog(msg, FileDialogFilter);
70+
var selectedFile = GetFileFromDialog(msg, FileDialogFilter);
7071

7172
if (!string.IsNullOrEmpty(selectedFile))
73+
{
7274
PluginsSettingsFilePath = selectedFile;
73-
75+
}
7476
// Nothing selected because user pressed cancel from the file dialog window
75-
if (string.IsNullOrEmpty(selectedFile))
76-
InstallEnvironment();
77+
else
78+
{
79+
var forceDownloadMessage = string.Format(
80+
API.GetTranslation("runtimeExecutableInvalidChooseDownload"),
81+
Language,
82+
EnvName,
83+
Environment.NewLine
84+
);
85+
86+
// Let users select valid path or choose to download
87+
while (string.IsNullOrEmpty(selectedFile))
88+
{
89+
if (API.ShowMsgBox(forceDownloadMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
90+
{
91+
// Continue select file
92+
selectedFile = GetFileFromDialog(msg, FileDialogFilter);
93+
}
94+
else
95+
{
96+
// User selected no, break the loop
97+
break;
98+
}
99+
}
100+
101+
if (!string.IsNullOrEmpty(selectedFile))
102+
{
103+
PluginsSettingsFilePath = selectedFile;
104+
}
105+
else
106+
{
107+
InstallEnvironment();
108+
}
109+
}
77110
}
78111
else
79112
{
@@ -86,7 +119,7 @@ internal IEnumerable<PluginPair> Setup()
86119
}
87120
else
88121
{
89-
API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
122+
API.ShowMsgBox(string.Format(API.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
90123
Log.Error("PluginsLoader",
91124
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
92125
$"{Language}Environment");
@@ -99,13 +132,11 @@ internal IEnumerable<PluginPair> Setup()
99132

100133
private void EnsureLatestInstalled(string expectedPath, string currentPath, string installedDirPath)
101134
{
102-
if (expectedPath == currentPath)
103-
return;
135+
if (expectedPath == currentPath) return;
104136

105137
FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s));
106138

107139
InstallEnvironment();
108-
109140
}
110141

111142
internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata);
@@ -126,7 +157,7 @@ private IEnumerable<PluginPair> SetPathForPluginPairs(string filePath, string la
126157
return pluginPairs;
127158
}
128159

129-
private string GetFileFromDialog(string title, string filter = "")
160+
private static string GetFileFromDialog(string title, string filter = "")
130161
{
131162
var dlg = new OpenFileDialog
132163
{
@@ -140,7 +171,6 @@ private string GetFileFromDialog(string title, string filter = "")
140171

141172
var result = dlg.ShowDialog();
142173
return result == DialogResult.OK ? dlg.FileName : string.Empty;
143-
144174
}
145175

146176
/// <summary>
@@ -183,31 +213,33 @@ public static void PreStartPluginExecutablePathUpdate(Settings settings)
183213
else
184214
{
185215
if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName))
216+
{
186217
settings.PluginSettings.PythonExecutablePath
187218
= GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath);
219+
}
188220

189221
if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName))
222+
{
190223
settings.PluginSettings.NodeExecutablePath
191224
= GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath);
225+
}
192226
}
193227
}
194228

195229
private static bool IsUsingPortablePath(string filePath, string pluginEnvironmentName)
196230
{
197-
if (string.IsNullOrEmpty(filePath))
198-
return false;
231+
if (string.IsNullOrEmpty(filePath)) return false;
199232

200233
// DataLocation.PortableDataPath returns the current portable path, this determines if an out
201234
// of date path is also a portable path.
202-
var portableAppEnvLocation = $"UserData\\{DataLocation.PluginEnvironments}\\{pluginEnvironmentName}";
235+
var portableAppEnvLocation = Path.Combine("UserData", DataLocation.PluginEnvironments, pluginEnvironmentName);
203236

204237
return filePath.Contains(portableAppEnvLocation);
205238
}
206239

207240
private static bool IsUsingRoamingPath(string filePath)
208241
{
209-
if (string.IsNullOrEmpty(filePath))
210-
return false;
242+
if (string.IsNullOrEmpty(filePath)) return false;
211243

212244
return filePath.StartsWith(DataLocation.RoamingDataPath);
213245
}
@@ -217,8 +249,8 @@ private static string GetUpdatedEnvironmentPath(string filePath)
217249
var index = filePath.IndexOf(DataLocation.PluginEnvironments);
218250

219251
// get the substring after "Environments" because we can not determine it dynamically
220-
var ExecutablePathSubstring = filePath.Substring(index + DataLocation.PluginEnvironments.Count());
221-
return $"{DataLocation.PluginEnvironmentsPath}{ExecutablePathSubstring}";
252+
var executablePathSubstring = filePath[(index + DataLocation.PluginEnvironments.Length)..];
253+
return $"{DataLocation.PluginEnvironmentsPath}{executablePathSubstring}";
222254
}
223255
}
224256
}

Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
using Droplex;
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using Droplex;
24
using Flow.Launcher.Core.Plugin;
35
using Flow.Launcher.Infrastructure.UserSettings;
46
using Flow.Launcher.Plugin;
57
using Flow.Launcher.Plugin.SharedCommands;
6-
using System.Collections.Generic;
7-
using System.IO;
88

99
namespace Flow.Launcher.Core.ExternalPlugins.Environments
1010
{
@@ -22,7 +22,11 @@ internal class PythonEnvironment : AbstractPluginEnvironment
2222

2323
internal override string FileDialogFilter => "Python|pythonw.exe";
2424

25-
internal override string PluginsSettingsFilePath { get => PluginSettings.PythonExecutablePath; set => PluginSettings.PythonExecutablePath = value; }
25+
internal override string PluginsSettingsFilePath
26+
{
27+
get => PluginSettings.PythonExecutablePath;
28+
set => PluginSettings.PythonExecutablePath = value;
29+
}
2630

2731
internal PythonEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
2832

Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using System.Collections.Generic;
2+
using System.IO;
23
using Droplex;
4+
using Flow.Launcher.Core.Plugin;
35
using Flow.Launcher.Infrastructure.UserSettings;
4-
using Flow.Launcher.Plugin.SharedCommands;
56
using Flow.Launcher.Plugin;
6-
using System.IO;
7-
using Flow.Launcher.Core.Plugin;
7+
using Flow.Launcher.Plugin.SharedCommands;
88

99
namespace Flow.Launcher.Core.ExternalPlugins.Environments
1010
{
@@ -19,7 +19,11 @@ internal class TypeScriptEnvironment : AbstractPluginEnvironment
1919
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
2020
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
2121

22-
internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; }
22+
internal override string PluginsSettingsFilePath
23+
{
24+
get => PluginSettings.NodeExecutablePath;
25+
set => PluginSettings.NodeExecutablePath = value;
26+
}
2327

2428
internal TypeScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
2529

Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using System.Collections.Generic;
2+
using System.IO;
23
using Droplex;
4+
using Flow.Launcher.Core.Plugin;
35
using Flow.Launcher.Infrastructure.UserSettings;
4-
using Flow.Launcher.Plugin.SharedCommands;
56
using Flow.Launcher.Plugin;
6-
using System.IO;
7-
using Flow.Launcher.Core.Plugin;
7+
using Flow.Launcher.Plugin.SharedCommands;
88

99
namespace Flow.Launcher.Core.ExternalPlugins.Environments
1010
{
@@ -19,7 +19,11 @@ internal class TypeScriptV2Environment : AbstractPluginEnvironment
1919
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
2020
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
2121

22-
internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; }
22+
internal override string PluginsSettingsFilePath
23+
{
24+
get => PluginSettings.NodeExecutablePath;
25+
set => PluginSettings.NodeExecutablePath = value;
26+
}
2327

2428
internal TypeScriptV2Environment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
2529

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
using Flow.Launcher.Core.ExternalPlugins;
2-
using System;
1+
using System;
32
using System.Collections.Concurrent;
43
using System.Collections.Generic;
54
using System.IO;
65
using System.Linq;
6+
using System.Text.Json;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using CommunityToolkit.Mvvm.DependencyInjection;
10+
using Flow.Launcher.Core.ExternalPlugins;
911
using Flow.Launcher.Infrastructure;
1012
using Flow.Launcher.Infrastructure.Logger;
1113
using Flow.Launcher.Infrastructure.UserSettings;
1214
using Flow.Launcher.Plugin;
13-
using ISavable = Flow.Launcher.Plugin.ISavable;
1415
using Flow.Launcher.Plugin.SharedCommands;
15-
using System.Text.Json;
16-
using Flow.Launcher.Core.Resource;
17-
using CommunityToolkit.Mvvm.DependencyInjection;
16+
using ISavable = Flow.Launcher.Plugin.ISavable;
1817

1918
namespace Flow.Launcher.Core.Plugin
2019
{

Flow.Launcher/App.xaml.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ private async void OnStartup(object sender, StartupEventArgs e)
138138
{
139139
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
140140
{
141+
// Because new message box api uses MessageBoxEx window,
142+
// if it is created and closed before main window is created, it will cause the application to exit.
143+
// So set to OnExplicitShutdown to prevent the application from shutting down before main window is created
144+
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
145+
141146
Log.SetLogLevel(_settings.LogLevel);
142147

143148
Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();

Flow.Launcher/Languages/en.xaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable
1010
</system:String>
1111
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Please select the {0} executable</system:String>
12+
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
13+
Your selected {0} executable is invalid.
14+
{2}{2}
15+
Click yes if you would like select the {0} executable agian. Click no if you would like to download {1}
16+
</system:String>
1217
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom).</system:String>
1318
<system:String x:Key="failedToInitializePluginsTitle">Fail to Init Plugins</system:String>
1419
<system:String x:Key="failedToInitializePluginsMessage">Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help</system:String>

Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,37 @@ public class ThemeSelector
99
{
1010
public const string Keyword = "fltheme";
1111

12-
private readonly Theme _theme;
1312
private readonly PluginInitContext _context;
1413

14+
// Do not initialize it in the constructor, because it will cause null reference in
15+
// var dicts = Application.Current.Resources.MergedDictionaries; line of Theme
16+
private Theme theme = null;
17+
private Theme Theme => theme ??= Ioc.Default.GetRequiredService<Theme>();
18+
1519
#region Theme Selection
1620

1721
// Theme select codes simplified from SettingsPaneThemeViewModel.cs
1822

1923
private Theme.ThemeData _selectedTheme;
2024
public Theme.ThemeData SelectedTheme
2125
{
22-
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.GetCurrentTheme());
26+
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == Theme.GetCurrentTheme());
2327
set
2428
{
2529
_selectedTheme = value;
26-
_theme.ChangeTheme(value.FileNameWithoutExtension);
30+
Theme.ChangeTheme(value.FileNameWithoutExtension);
2731

28-
_ = _theme.RefreshFrameAsync();
32+
_ = Theme.RefreshFrameAsync();
2933
}
3034
}
3135

32-
private List<Theme.ThemeData> Themes => _theme.LoadAvailableThemes();
36+
private List<Theme.ThemeData> Themes => Theme.LoadAvailableThemes();
3337

3438
#endregion
3539

3640
public ThemeSelector(PluginInitContext context)
3741
{
3842
_context = context;
39-
_theme = Ioc.Default.GetRequiredService<Theme>();
4043
}
4144

4245
public List<Result> Query(Query query)

0 commit comments

Comments
 (0)