From cf2947dd946fa9d64eb3562a470ab41eaa52083d Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Fri, 1 May 2020 17:40:12 +0300 Subject: [PATCH 001/142] remove unused includes in project files --- Flow.Launcher.Test/Flow.Launcher.Test.csproj | 4 ---- .../Flow.Launcher.Plugin.BrowserBookmark.csproj | 4 ---- .../Flow.Launcher.Plugin.Calculator.csproj | 1 - .../Flow.Launcher.Plugin.Color.csproj | 4 ---- .../Flow.Launcher.Plugin.ControlPanel.csproj | 4 ---- .../Flow.Launcher.Plugin.Everything.csproj | 4 ---- .../Flow.Launcher.Plugin.Folder.csproj | 4 ---- .../Flow.Launcher.Plugin.PluginIndicator.csproj | 6 +----- .../Flow.Launcher.Plugin.PluginManagement.csproj | 4 ---- .../Flow.Launcher.Plugin.Program.csproj | 5 +---- .../Flow.Launcher.Plugin.Shell.csproj | 4 ---- .../Flow.Launcher.Plugin.Sys.csproj | 1 - .../Flow.Launcher.Plugin.Url.csproj | 4 ---- .../Flow.Launcher.Plugin.WebSearch.csproj | 4 ---- Plugins/HelloWorldCSharp/HelloWorldCSharp.csproj | 4 ---- 15 files changed, 2 insertions(+), 55 deletions(-) diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 2dbf1a9d3..eb26fccf2 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -56,8 +56,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index b59b114fe..13daddf10 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -80,8 +80,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index d0bc45383..e7cae42ae 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -99,7 +99,6 @@ - diff --git a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj b/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj index fb03fda79..19f8fb980 100644 --- a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj +++ b/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj @@ -101,8 +101,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj index c75b7aa38..d1c185c36 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj @@ -101,8 +101,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Everything/Flow.Launcher.Plugin.Everything.csproj b/Plugins/Flow.Launcher.Plugin.Everything/Flow.Launcher.Plugin.Everything.csproj index 3fde34b5d..41ad9007c 100644 --- a/Plugins/Flow.Launcher.Plugin.Everything/Flow.Launcher.Plugin.Everything.csproj +++ b/Plugins/Flow.Launcher.Plugin.Everything/Flow.Launcher.Plugin.Everything.csproj @@ -128,8 +128,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Folder/Flow.Launcher.Plugin.Folder.csproj b/Plugins/Flow.Launcher.Plugin.Folder/Flow.Launcher.Plugin.Folder.csproj index 67cc0294c..a05b5d49d 100644 --- a/Plugins/Flow.Launcher.Plugin.Folder/Flow.Launcher.Plugin.Folder.csproj +++ b/Plugins/Flow.Launcher.Plugin.Folder/Flow.Launcher.Plugin.Folder.csproj @@ -104,8 +104,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index a29c720f1..48639156e 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -101,9 +101,5 @@ - - - - - + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj b/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj index aa180cca4..49451d5ba 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj @@ -103,8 +103,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index 6208881d4..331566f90 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -113,8 +113,5 @@ - - - - + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj index b50295ccd..ad1dd079e 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj +++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj @@ -99,8 +99,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index 80ed0e56b..b63654b7c 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -124,7 +124,6 @@ - diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj index 6bc8154af..75fa52290 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj +++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj @@ -93,9 +93,5 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index d159e9bc0..c2449a49e 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -151,8 +151,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/HelloWorldCSharp/HelloWorldCSharp.csproj b/Plugins/HelloWorldCSharp/HelloWorldCSharp.csproj index 8f1867db5..2e2ab78b5 100644 --- a/Plugins/HelloWorldCSharp/HelloWorldCSharp.csproj +++ b/Plugins/HelloWorldCSharp/HelloWorldCSharp.csproj @@ -51,9 +51,5 @@ PreserveNewest - - - - \ No newline at end of file From 85d2b3f5228dc1e56bf592b647230362142b5ffd Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Fri, 1 May 2020 18:31:28 +0300 Subject: [PATCH 002/142] update appveyor.yml --- appveyor.yml | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a676731b9..6b600f33b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,31 +1,25 @@ -version: 1.3.{build} -image: Visual Studio 2017 +version: 1.0.{build} +image: Visual Studio 2019 configuration: Release platform: Any CPU assembly_info: patch: true - file: AssemblyInfo.* + file: SolutionAssemblyInfo.cs assembly_version: '{version}' assembly_file_version: '{version}' assembly_informational_version: '{version}' +dotnet_csproj: + patch: true + file: 'Flow.*\*.csproj' + version: '{version}' + package_version: '{version}' + assembly_version: '{version}' + file_version: '{version}' + informational_version: '{version}' before_build: - ps: nuget restore build: project: Flow.Launcher.sln after_test: artifacts: -- path: 'Output\Packages\Flow.Launcher-*.zip' - name: zipped_binary -- path: 'Output\Packages\Flow.Launcher.Plugin.*.nupkg' - name: nuget_package -- path: 'Output\Packages\Flow.Launcher-*.*' - name: installer -- path: 'Output\Packages\RELEASES' - name: installer -deploy: - provider: NuGet - api_key: - secure: yybUOFgBuGVpbmOVZxsurC8OpkClzt9dR+/54WpMWcq6b6oyMatciaelRPnXsjRn - artifact: nuget_package - on: - branch: api \ No newline at end of file +- path: 'Output\Packages\*' \ No newline at end of file From fed56d26ad84ec4554f7a8eba1b0cb6b41137827 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Fri, 1 May 2020 22:24:28 +0300 Subject: [PATCH 003/142] update the post build script * use dotnet pack for the plugin project * refactor Zip-Release and Pack-Squirrel-Installer functions * remove reference to NuGet.CommandLine - no longer needed --- Flow.Launcher/Flow.Launcher.csproj | 4 ---- Scripts/flowlauncher.nuspec | 2 +- Scripts/post_build.ps1 | 27 +++++++++++++-------------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 7049d9c4d..679cf22a3 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -61,10 +61,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Scripts/flowlauncher.nuspec b/Scripts/flowlauncher.nuspec index cfac20b06..a4831f4db 100644 --- a/Scripts/flowlauncher.nuspec +++ b/Scripts/flowlauncher.nuspec @@ -11,6 +11,6 @@ Flow Launcher - a launcher for windows - + diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 14faf82b1..e411b49be 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -57,14 +57,15 @@ function Validate-Directory ($output) { New-Item $output -ItemType Directory -Force } -function Pack-Nuget ($path, $version, $output) { +function Pack-Plugin ($path, $output) { Write-Host "Begin build nuget library" - $spec = "$path\Scripts\flowlauncher.plugin.nuspec" - Write-Host "nuspec path: $spec" + $project = "$path\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" + + Write-Host "Packing: $project" Write-Host "Output path: $output" - Nuget pack $spec -Version $version -OutputDirectory $output + dotnet pack $project --include-symbols --configuration Release --output $output Write-Host "End build nuget library" } @@ -72,13 +73,10 @@ function Pack-Nuget ($path, $version, $output) { function Zip-Release ($path, $version, $output) { Write-Host "Begin zip release" - $input = "$path\Output\Release" - Write-Host "Input path: $input" - $file = "$output\Flow.Launcher-$version.zip" - Write-Host "Filename: $file" + $content = "$path\Output\Release\*" + $zipFile = "$output\Flow.Launcher-$version.zip" - [Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") - [System.IO.Compression.ZipFile]::CreateFromDirectory($input, $file) + Compress-Archive -Force -Path $content -DestinationPath $zipFile Write-Host "End zip release" } @@ -88,10 +86,12 @@ function Pack-Squirrel-Installer ($path, $version, $output) { Write-Host "Begin pack squirrel installer" $spec = "$path\Scripts\flowlauncher.nuspec" - Write-Host "nuspec path: $spec" $input = "$path\Output\Release" + + Write-Host "Packing: $spec" Write-Host "Input path: $input" - Nuget pack $spec -Version $version -Properties Configuration=Release -BasePath $input -OutputDirectory $output + # TODO: can we use dotnet pack here? + nuget pack $spec -Version $version -BasePath $input -OutputDirectory $output -Properties Configuration=Release $nupkg = "$output\FlowLauncher.$version.nupkg" Write-Host "nupkg path: $nupkg" @@ -139,12 +139,11 @@ function Main { $o = "$p\Output\Packages" Validate-Directory $o # making version static as multiple versions can exist in the nuget folder and in the case a breaking change is introduced. - New-Alias Nuget $env:USERPROFILE\.nuget\packages\NuGet.CommandLine\5.4.0\tools\NuGet.exe -Force Pack-Squirrel-Installer $p $v $o $isInCI = $env:APPVEYOR if ($isInCI) { - Pack-Nuget $p $v $o + Pack-Plugin $p $o Zip-Release $p $v $o } From 6ef4c04b97e2a627530b22199f77746e96231475 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Fri, 1 May 2020 22:34:40 +0300 Subject: [PATCH 004/142] add package details to Flow.Launcher.Plugin.csproj --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 82fbb31d5..e8f5769b4 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -12,7 +12,21 @@ false false - + + + 1.0.0 + 1.0.0 + 1.0.0 + 1.0.0 + Flow.Launcher.Plugin + true + qianlifeng, Jeremy Wu + MIT + https://github.com/Flow-Launcher/Flow.Launcher + Reference this library if you want to develop a Flow Launcher plugin + flowlauncher + + true full From 2d6dd979213aef1353ad88f680c188769992fb70 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Mon, 4 May 2020 14:33:11 +0300 Subject: [PATCH 005/142] pack Plugin project on Release builds also use the new format for package symbols (snupkg) --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index e8f5769b4..13a4652c6 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -20,11 +20,13 @@ 1.0.0 Flow.Launcher.Plugin true + snupkg qianlifeng, Jeremy Wu MIT https://github.com/Flow-Launcher/Flow.Launcher Reference this library if you want to develop a Flow Launcher plugin flowlauncher + true From f57fdfbc50d869f04d58ba3e5991f84acc58068d Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Mon, 4 May 2020 14:37:26 +0300 Subject: [PATCH 006/142] appveyor: update path to Plugin artifacts and remove the pack step from the post-build script (no longer needed) --- Scripts/post_build.ps1 | 14 -------------- appveyor.yml | 6 +++++- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index e411b49be..5d9a0baf6 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -57,19 +57,6 @@ function Validate-Directory ($output) { New-Item $output -ItemType Directory -Force } -function Pack-Plugin ($path, $output) { - Write-Host "Begin build nuget library" - - $project = "$path\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" - - Write-Host "Packing: $project" - Write-Host "Output path: $output" - - dotnet pack $project --include-symbols --configuration Release --output $output - - Write-Host "End build nuget library" -} - function Zip-Release ($path, $version, $output) { Write-Host "Begin zip release" @@ -143,7 +130,6 @@ function Main { $isInCI = $env:APPVEYOR if ($isInCI) { - Pack-Plugin $p $o Zip-Release $p $v $o } diff --git a/appveyor.yml b/appveyor.yml index 6b600f33b..55bd2ed6b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,4 +22,8 @@ build: project: Flow.Launcher.sln after_test: artifacts: -- path: 'Output\Packages\*' \ No newline at end of file +- path: 'Output\Packages\*' +- path: 'Output\Release\Flow.Launcher.Plugin.*.nupkg' + name: Plugin nupkg +- path: 'Output\Release\Flow.Launcher.Plugin.*.snupkg' + name: Plugin snupkg \ No newline at end of file From 53bc0f2cba6e2e30cff7c5366fe4cf08394cdc8d Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Mon, 4 May 2020 14:56:35 +0300 Subject: [PATCH 007/142] plugin: set Authors to the nuget org name --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 13a4652c6..7c026eba4 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -21,7 +21,7 @@ Flow.Launcher.Plugin true snupkg - qianlifeng, Jeremy Wu + flow-launcher MIT https://github.com/Flow-Launcher/Flow.Launcher Reference this library if you want to develop a Flow Launcher plugin From 9737d2fc70c5ecec579260880c632a075719f008 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Mon, 4 May 2020 15:14:47 +0300 Subject: [PATCH 008/142] delete nuspec file for the plugin project no longer needed --- Flow.Launcher.sln | 1 - Scripts/flowlauncher.plugin.nuspec | 16 ---------------- 2 files changed, 17 deletions(-) delete mode 100644 Scripts/flowlauncher.plugin.nuspec diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 2827cf585..f668b2764 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -63,7 +63,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs Scripts\flowlauncher.nuspec = Scripts\flowlauncher.nuspec - Scripts\flowlauncher.plugin.nuspec = Scripts\flowlauncher.plugin.nuspec EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloWorldCSharp", "Plugins\HelloWorldCSharp\HelloWorldCSharp.csproj", "{03FFA443-5F50-48D5-8869-F3DF316803AA}" diff --git a/Scripts/flowlauncher.plugin.nuspec b/Scripts/flowlauncher.plugin.nuspec deleted file mode 100644 index 68b60a29f..000000000 --- a/Scripts/flowlauncher.plugin.nuspec +++ /dev/null @@ -1,16 +0,0 @@ - - - - Flow.Launcher.Plugin - $version$ - qianlifeng, Jeremy Wu - https://github.com/Flow-Launcher/Flow.Launcher/blob/master/LICENSE - https://github.com/Flow-Launcher/Flow.Launcher - false - Reference this library if you want to develop a Flow Launcher plugin - flowlauncher - - - - - From 658b9d721bae4cf1f4d8edf7bd5883c93d5dc34c Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Tue, 5 May 2020 13:13:17 +0300 Subject: [PATCH 009/142] plugin: add SourceLink support --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 7c026eba4..04444c7b8 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -27,6 +27,7 @@ Reference this library if you want to develop a Flow Launcher plugin flowlauncher true + true @@ -68,6 +69,7 @@ + From b63521bff3548040f55b225e4231bf512ddc782e Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Fri, 8 May 2020 23:07:05 +0300 Subject: [PATCH 010/142] update appveyor.yml * make the file easier to read * minimal build output verbosity * skip commits that edit markdown files --- appveyor.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 55bd2ed6b..d1ce37bb9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,13 +1,12 @@ version: 1.0.{build} -image: Visual Studio 2019 -configuration: Release -platform: Any CPU + assembly_info: patch: true file: SolutionAssemblyInfo.cs assembly_version: '{version}' assembly_file_version: '{version}' assembly_informational_version: '{version}' + dotnet_csproj: patch: true file: 'Flow.*\*.csproj' @@ -16,11 +15,20 @@ dotnet_csproj: assembly_version: '{version}' file_version: '{version}' informational_version: '{version}' + +skip_commits: + files: + - '*.md' + +image: Visual Studio 2019 +platform: Any CPU +configuration: Release before_build: - ps: nuget restore build: project: Flow.Launcher.sln -after_test: + verbosity: minimal + artifacts: - path: 'Output\Packages\*' - path: 'Output\Release\Flow.Launcher.Plugin.*.nupkg' From 12bcb6bb89a6bee006eedc80981f78d512a1b407 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Sat, 9 May 2020 00:15:54 +0300 Subject: [PATCH 011/142] appveyor: updating naming of artifacts --- Scripts/flowlauncher.nuspec | 2 +- Scripts/post_build.ps1 | 6 +++--- appveyor.yml | 9 ++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Scripts/flowlauncher.nuspec b/Scripts/flowlauncher.nuspec index a4831f4db..ec953399c 100644 --- a/Scripts/flowlauncher.nuspec +++ b/Scripts/flowlauncher.nuspec @@ -1,7 +1,7 @@ - FlowLauncher + Flow-Launcher Flow Launcher $version$ happlebao, Jeremy Wu diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 5d9a0baf6..36cda54e6 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -61,7 +61,7 @@ function Zip-Release ($path, $version, $output) { Write-Host "Begin zip release" $content = "$path\Output\Release\*" - $zipFile = "$output\Flow.Launcher-$version.zip" + $zipFile = "$output\Flow-Launcher-v$version.zip" Compress-Archive -Force -Path $content -DestinationPath $zipFile @@ -80,7 +80,7 @@ function Pack-Squirrel-Installer ($path, $version, $output) { # TODO: can we use dotnet pack here? nuget pack $spec -Version $version -BasePath $input -OutputDirectory $output -Properties Configuration=Release - $nupkg = "$output\FlowLauncher.$version.nupkg" + $nupkg = "$output\Flow-Launcher.$version.nupkg" Write-Host "nupkg path: $nupkg" $icon = "$path\Flow.Launcher\Resources\app.ico" Write-Host "icon: $icon" @@ -94,7 +94,7 @@ function Pack-Squirrel-Installer ($path, $version, $output) { Move-Item $temp\* $output -Force Remove-Item $temp - $file = "$output\Flow Launcher-$version.exe" + $file = "$output\Flow-Launcher-v$version.exe" Write-Host "Filename: $file" Move-Item "$output\Setup.exe" $file -Force diff --git a/appveyor.yml b/appveyor.yml index d1ce37bb9..dc98a7a20 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -30,7 +30,14 @@ build: verbosity: minimal artifacts: -- path: 'Output\Packages\*' +- path: 'Output\Packages\*.exe' + name: Setup File +- path: 'Output\Packages\*.nupkg' + name: Squirrel nupkg +- path: 'Output\Packages\RELEASES' + name: Squirrel RELEASES +- path: 'Output\Packages\Flow-Launcher-*.zip' + name: Zip - path: 'Output\Release\Flow.Launcher.Plugin.*.nupkg' name: Plugin nupkg - path: 'Output\Release\Flow.Launcher.Plugin.*.snupkg' From 76e71368ad3c9c1ada7c9eaa5391efa035eeb73a Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Sat, 9 May 2020 00:19:17 +0300 Subject: [PATCH 012/142] appveyor: update versioning --- Scripts/post_build.ps1 | 4 ++-- appveyor.yml | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 36cda54e6..d5cb947b4 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -6,10 +6,10 @@ param( Write-Host "Config: $config" function Build-Version { - if ([string]::IsNullOrEmpty($env:APPVEYOR_BUILD_VERSION)) { + if ([string]::IsNullOrEmpty($env:flowVersion)) { $v = (Get-Command ${TargetPath}).FileVersionInfo.FileVersion } else { - $v = $env:APPVEYOR_BUILD_VERSION + $v = $env:flowVersion } Write-Host "Build Version: $v" diff --git a/appveyor.yml b/appveyor.yml index dc98a7a20..243e3ed2c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,20 +1,24 @@ -version: 1.0.{build} +version: '1.0.0.{build}' + +init: +- ps: | + $version = new-object System.Version $env:APPVEYOR_BUILD_VERSION + $env:flowVersion = "{0}.{1}.{2}" -f $version.Major, $version.Minor, $version.Build assembly_info: patch: true file: SolutionAssemblyInfo.cs - assembly_version: '{version}' - assembly_file_version: '{version}' - assembly_informational_version: '{version}' - + assembly_version: $(flowVersion) + assembly_file_version: $(flowVersion) + assembly_informational_version: $(flowVersion) dotnet_csproj: patch: true file: 'Flow.*\*.csproj' - version: '{version}' - package_version: '{version}' - assembly_version: '{version}' - file_version: '{version}' - informational_version: '{version}' + version: $(flowVersion) + package_version: $(flowVersion) + assembly_version: $(flowVersion) + file_version: $(flowVersion) + informational_version: $(flowVersion) skip_commits: files: From 8848ed1a00b70b9c72eeabec19eb212eb7c96cc4 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 11 May 2020 23:15:15 +1000 Subject: [PATCH 013/142] Add skeleton tests and classes for Explorer --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 16 +++++ Flow.Launcher.sln | 18 +++++- .../Flow.Launcher.Plugin.Explorer.csproj | 24 ++++++++ .../Images/explorer.png | Bin 0 -> 44763 bytes Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 57 ++++++++++++++++++ .../Flow.Launcher.Plugin.Explorer/Settings.cs | 10 +++ .../ViewModels/SettingsViewModel.cs | 25 ++++++++ .../Views/ExplorerSettings.xaml | 12 ++++ .../Views/ExplorerSettings.xaml.cs | 27 +++++++++ .../Flow.Launcher.Plugin.Explorer/plugin.json | 12 ++++ 10 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 Flow.Launcher.Test/Plugins/ExplorerTest.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/explorer.png create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Main.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/plugin.json diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs new file mode 100644 index 000000000..2bc95d086 --- /dev/null +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -0,0 +1,16 @@ +using NUnit.Framework; + +namespace Flow.Launcher.Test.Plugins +{ + [TestFixture] + public class ExplorerTest + { + public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string expectedString){} + + public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString() { } + + public void GivenWindowsIndexSearch_WhenReturnedNilAndIsNotIndexed_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() { } + + public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString() { } + } +} diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 2827cf585..c242932ea 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29806.167 @@ -18,6 +18,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc {1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0} {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4} + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E} {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} {787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {787B8AA6-CA93-4C84-96FE-DF31110AD1C4} @@ -76,6 +77,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Calcul EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "HelloWorldFSharp", "Plugins\HelloWorldFSharp\HelloWorldFSharp.fsproj", "{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Explorer", "Plugins\Flow.Launcher.Plugin.Explorer\Flow.Launcher.Plugin.Explorer.csproj", "{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -327,6 +330,18 @@ Global {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x64.Build.0 = Release|Any CPU {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x86.ActiveCfg = Release|Any CPU {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x86.Build.0 = Release|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|x64.ActiveCfg = Debug|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|x64.Build.0 = Debug|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|x86.ActiveCfg = Debug|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|x86.Build.0 = Debug|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|Any CPU.Build.0 = Release|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x64.ActiveCfg = Release|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x64.Build.0 = Release|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x86.ActiveCfg = Release|Any CPU + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -347,6 +362,7 @@ Global {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj new file mode 100644 index 000000000..5f1ec0544 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -0,0 +1,24 @@ + + + + Library + netcoreapp3.1 + true + + + + + + ..\..\Output\Debug\Plugins\Flow.Launcher.Explorer + + + + ..\..\Output\Release\Plugins\Flow.Launcher.Explorer + + + + + + + + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Images/explorer.png b/Plugins/Flow.Launcher.Plugin.Explorer/Images/explorer.png new file mode 100644 index 0000000000000000000000000000000000000000..552b2c5bd1715bce33e57cd2f4f8927d03879912 GIT binary patch literal 44763 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 TjU5kzfbZ!OoyVnWmS6q{5RG1u literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs new file mode 100644 index 000000000..206911b6c --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -0,0 +1,57 @@ +using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Plugin.Explorer.ViewModels; +using Flow.Launcher.Plugin.Explorer.Views; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Controls; + +namespace Flow.Launcher.Plugin.Explorer +{ + class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContextMenu + { + private PluginInitContext Context { get; set; } + + private Settings _settings; + + private SettingsViewModel _viewModel; + + public Control CreateSettingPanel() + { + return new ExplorerSettings(); + } + + public string GetTranslatedPluginDescription() + { + throw new NotImplementedException(); + } + + public string GetTranslatedPluginTitle() + { + throw new NotImplementedException(); + } + + public void Init(PluginInitContext context) + { + Context = context; + + _viewModel = new SettingsViewModel(); + _settings = _viewModel.Settings; + } + + public List LoadContextMenus(Result selectedResult) + { + throw new NotImplementedException(); + } + + public List Query(Query query) + { + return new List(); + } + + public void Save() + { + _viewModel.Save(); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs new file mode 100644 index 000000000..dcdb8f06e --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Flow.Launcher.Plugin.Explorer +{ + public class Settings + { + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs new file mode 100644 index 000000000..298476abd --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -0,0 +1,25 @@ +using Flow.Launcher.Infrastructure.Storage; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Flow.Launcher.Plugin.Explorer.ViewModels +{ + public class SettingsViewModel + { + private readonly PluginJsonStorage _storage; + + public Settings Settings { get; set; } + + public SettingsViewModel() + { + _storage = new PluginJsonStorage(); + Settings = _storage.Load(); + } + + public void Save() + { + _storage.Save(); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml new file mode 100644 index 000000000..93defd985 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs new file mode 100644 index 000000000..f87c29412 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs @@ -0,0 +1,27 @@ +using Flow.Launcher.Plugin.Explorer.ViewModels; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Flow.Launcher.Plugin.Explorer.Views +{ + ///

+ /// Interaction logic for ExplorerSettings.xaml + /// + public partial class ExplorerSettings : UserControl + { + public ExplorerSettings() + { + InitializeComponent(); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json new file mode 100644 index 000000000..4a6c8785c --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -0,0 +1,12 @@ +{ + "ID": "572be03c74c642baae319fc283e561a8", + "ActionKeyword": "*", + "Name": "Explorer", + "Description": "Search and manage files, folders and subfolders. Explorer utilises Windows Index Search", + "Author": "Jeremy Wu", + "Version": "1.0.0", + "Language": "csharp", + "Website": "https://github.com/Flow-Launcher/Flow.Launcher", + "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", + "IcoPath": "Images\\explorer.png" +} From 6d7aa46747ee730679e07151bfc4ef0c5bd19d01 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 11 May 2020 23:19:41 +1000 Subject: [PATCH 014/142] Add Windows Index directory search query test --- Flow.Launcher.Test/Flow.Launcher.Test.csproj | 1 + Flow.Launcher.Test/Plugins/ExplorerTest.cs | 19 +++++++++++++- .../Search/WindowsIndex/QueryConstructor.cs | 26 +++++++++++++++++++ .../Flow.Launcher.Plugin.Explorer/Settings.cs | 1 + 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 2dbf1a9d3..e5ca36289 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -39,6 +39,7 @@
+ diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 2bc95d086..c0fa32f5b 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -1,11 +1,28 @@ +using Flow.Launcher.Plugin.Explorer; +using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using NUnit.Framework; +using System; namespace Flow.Launcher.Test.Plugins { [TestFixture] public class ExplorerTest { - public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string expectedString){} + [TestCase("directory='file:{path}'")] + public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + // When + var path = @"C:\Dropbox"; + var result = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(path); + + // Then + Assert.IsTrue(result == expectedString, + $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + + $"Actual: {result}{Environment.NewLine}"); + } public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString() { } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs new file mode 100644 index 000000000..9d5ffdb30 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex +{ + public class QueryConstructor + { + private Settings _settings; + + public QueryConstructor(Settings settings) + { + _settings = settings; + } + + /// + /// Search will be performed on all folders and files on the first level of a specified directory. + /// + public string QueryWhereRestrictionsForTopLevelDirectorySearch(string path) + { + // Set query restriction for top level directory search + return $"directory='file:{path}'"; + } + + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index dcdb8f06e..2d664c3db 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -6,5 +6,6 @@ namespace Flow.Launcher.Plugin.Explorer { public class Settings { + public int MaxResult { get; set; } = 100; } } From 5ed5d52733540c59e163f4dd2197cb7b344a25d9 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Mon, 11 May 2020 19:49:39 +0300 Subject: [PATCH 015/142] post-build script: remove leftover comment also convert indentation to spaces --- Scripts/post_build.ps1 | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index d5cb947b4..2aa6a1e87 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -1,14 +1,14 @@ param( [string]$config = "Release", [string]$solution, - [string]$targetpath + [string]$targetpath ) Write-Host "Config: $config" function Build-Version { - if ([string]::IsNullOrEmpty($env:flowVersion)) { - $v = (Get-Command ${TargetPath}).FileVersionInfo.FileVersion - } else { + if ([string]::IsNullOrEmpty($env:flowVersion)) { + $v = (Get-Command ${TargetPath}).FileVersionInfo.FileVersion + } else { $v = $env:flowVersion } @@ -120,12 +120,11 @@ function Main { if(IsDotNetCoreAppSelfContainedPublishEvent) { FixPublishLastWriteDateTimeError $p - } + } Delete-Unused $p $config $o = "$p\Output\Packages" Validate-Directory $o - # making version static as multiple versions can exist in the nuget folder and in the case a breaking change is introduced. Pack-Squirrel-Installer $p $v $o $isInCI = $env:APPVEYOR From 634e2630865822d7f4591374bf733bc303201084 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 12 May 2020 05:56:51 +1000 Subject: [PATCH 016/142] Correct test case param --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index c0fa32f5b..055a358bc 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -8,15 +8,15 @@ namespace Flow.Launcher.Test.Plugins [TestFixture] public class ExplorerTest { - [TestCase("directory='file:{path}'")] - public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string expectedString) + [TestCase("C:\\Dropbox", "directory='file:C:\\Dropbox'")] + public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString) { // Given var queryConstructor = new QueryConstructor(new Settings()); // When - var path = @"C:\Dropbox"; - var result = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(path); + var folderPath = path; + var result = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(folderPath); // Then Assert.IsTrue(result == expectedString, From 26f0ce2778346f19c3e5a5f402c20cba61746241 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 12 May 2020 06:47:45 +1000 Subject: [PATCH 017/142] Add base query constructor --- .../Search/WindowsIndex/QueryConstructor.cs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index 9d5ffdb30..db2d80858 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Search.Interop; +using System; using System.Collections.Generic; using System.Text; @@ -8,11 +9,39 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { private Settings _settings; + private const string SystemIndex = "SystemIndex"; + public QueryConstructor(Settings settings) { _settings = settings; } + public CSearchQueryHelper CreateBaseQuery() + { + // This uses the Microsoft.Search.Interop assembly + CSearchManager manager = new CSearchManager(); + + // SystemIndex catalog is the default catalog in Windows + ISearchCatalogManager catalogManager = manager.GetCatalog(SystemIndex); + + // Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer + var baseQuery = catalogManager.GetQueryHelper(); + + // Set the number of results we want. Don't set this property if all results are needed. + baseQuery.QueryMaxResults = _settings.MaxResult; + + // Set list of columns we want to display, getting the path presently + baseQuery.QuerySelectColumns = "System.FileName, System.ItemPathDisplay"; + + // Filter based on folder/file name + baseQuery.QueryContentProperties = "System.FileName"; + + // Set sorting order + //baseQuery.QuerySorting = "System.ItemType DESC"; + + return baseQuery; + } + /// /// Search will be performed on all folders and files on the first level of a specified directory. /// From f4ae5badf511c2eb5f015f9f586931a4f99d43e2 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 12 May 2020 06:50:17 +1000 Subject: [PATCH 018/142] Add test for directory search query string and method --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 15 +++++++++++++++ .../Flow.Launcher.Plugin.Explorer.csproj | 4 ++++ .../Search/WindowsIndex/QueryConstructor.cs | 11 ++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 055a358bc..18426bafb 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -24,6 +24,21 @@ namespace Flow.Launcher.Test.Plugins $"Actual: {result}{Environment.NewLine}"); } + [TestCase("C:\\Dropbox", "SELECT TOP 100 System.FileName, System.ItemPathDisplay FROM SystemIndex WHERE directory='file:C:\\Dropbox'")] + public void GivenWindowsIndexSearch_WhenSearchTypeIsSearchTopFolderLevel_ThenQueryShouldUseExpectedString(string folderPath, string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + //When + var queryString = queryConstructor.QueryForTopLevelDirectorySearch(folderPath); + + Assert.IsTrue(queryString == expectedString, + $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + + $"Actual string was: {queryString}{Environment.NewLine}"); + } + + public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString() { } public void GivenWindowsIndexSearch_WhenReturnedNilAndIsNotIndexed_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() { } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 5f1ec0544..821c92714 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -16,6 +16,10 @@ ..\..\Output\Release\Plugins\Flow.Launcher.Explorer
+ + + + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index db2d80858..ab3d529ef 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -1,4 +1,4 @@ -using Microsoft.Search.Interop; +using Microsoft.Search.Interop; using System; using System.Collections.Generic; using System.Text; @@ -51,5 +51,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return $"directory='file:{path}'"; } + public string QueryForTopLevelDirectorySearch(string folderPath) + { + string query = "SELECT TOP " + _settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; + + query += QueryWhereRestrictionsForTopLevelDirectorySearch(folderPath); + + return query; + } + } } From 595d8d33c4e226ef596f1affc043e1244ee226db Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Wed, 13 May 2020 01:33:36 +0300 Subject: [PATCH 019/142] update package properties --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 4 ++-- Scripts/flowlauncher.nuspec | 2 +- Scripts/post_build.ps1 | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 04444c7b8..7965f75c6 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -21,7 +21,7 @@ Flow.Launcher.Plugin true snupkg - flow-launcher + Flow-Launcher MIT https://github.com/Flow-Launcher/Flow.Launcher Reference this library if you want to develop a Flow Launcher plugin diff --git a/Scripts/flowlauncher.nuspec b/Scripts/flowlauncher.nuspec index ec953399c..a4831f4db 100644 --- a/Scripts/flowlauncher.nuspec +++ b/Scripts/flowlauncher.nuspec @@ -1,7 +1,7 @@ - Flow-Launcher + FlowLauncher Flow Launcher $version$ happlebao, Jeremy Wu diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 2aa6a1e87..16f27476f 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -80,7 +80,7 @@ function Pack-Squirrel-Installer ($path, $version, $output) { # TODO: can we use dotnet pack here? nuget pack $spec -Version $version -BasePath $input -OutputDirectory $output -Properties Configuration=Release - $nupkg = "$output\Flow-Launcher.$version.nupkg" + $nupkg = "$output\FlowLauncher.$version.nupkg" Write-Host "nupkg path: $nupkg" $icon = "$path\Flow.Launcher\Resources\app.ico" Write-Host "icon: $icon" From e87a3a2e614a3a125663053fcf905b8fdc8890b3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 18 May 2020 20:13:31 +1000 Subject: [PATCH 020/142] Update method comments --- .../Search/WindowsIndex/QueryConstructor.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index ab3d529ef..41b8040f3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -1,4 +1,4 @@ -using Microsoft.Search.Interop; +using Microsoft.Search.Interop; using System; using System.Collections.Generic; using System.Text; @@ -43,14 +43,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex } /// - /// Search will be performed on all folders and files on the first level of a specified directory. + /// Set the required WHERE clause restriction to search on the first level of a specified directory. /// public string QueryWhereRestrictionsForTopLevelDirectorySearch(string path) { - // Set query restriction for top level directory search return $"directory='file:{path}'"; } + /// + /// Search will be performed on all folders and files on the first level of a specified directory. + /// public string QueryForTopLevelDirectorySearch(string folderPath) { string query = "SELECT TOP " + _settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; From ff03f7d284739be6ea78549ca9f677e7daa735fe Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 18 May 2020 20:14:38 +1000 Subject: [PATCH 021/142] Add tests and methods for all folders and files search --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 32 +++++++++++++++++-- .../Search/WindowsIndex/QueryConstructor.cs | 18 ++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 18426bafb..f4028b27d 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -37,9 +37,37 @@ namespace Flow.Launcher.Test.Plugins $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + $"Actual string was: {queryString}{Environment.NewLine}"); } - - public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString() { } + [TestCase("scope='file:'")] + public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + //When + var resultString = queryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch(); + + Assert.IsTrue(resultString == expectedString, + $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + + $"Actual string was: {resultString}{Environment.NewLine}"); + } + + [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\" " + + "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:'")] + public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString( + string userSearchString, string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + //When + var resultString = queryConstructor.QueryForAllFilesAndFolders(userSearchString); + + Assert.IsTrue(resultString == expectedString, + $"Expected query string: {expectedString}{Environment.NewLine} " + + $"Actual string was: {resultString}{Environment.NewLine}"); + } public void GivenWindowsIndexSearch_WhenReturnedNilAndIsNotIndexed_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() { } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index 41b8040f3..b311b0b1d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -1,4 +1,4 @@ -using Microsoft.Search.Interop; +using Microsoft.Search.Interop; using System; using System.Collections.Generic; using System.Text; @@ -62,5 +62,21 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return query; } + /// + /// Search will be performed on all folders and files based on user's search keywords. + /// + public string QueryForAllFilesAndFolders(string userSearchString) + { + // Generate SQL from our parameters, converting the userQuery from AQS->WHERE clause + return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch(); + } + + /// + /// Set the required WHERE clause restriction to search for all files and folders. + /// + public string QueryWhereRestrictionsForAllFilesAndFoldersSearch() + { + return $"scope='file:'"; + } } } From 5484b305142acb4d79815307a0b918eb6c521290 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 18 May 2020 21:31:28 +1000 Subject: [PATCH 022/142] Add OleDb connection method --- .../Flow.Launcher.Plugin.Explorer.csproj | 1 + .../Search/WindowsIndex/QueryConstructor.cs | 68 ++++++++++++++++--- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 821c92714..b5a192c90 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -17,6 +17,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index b311b0b1d..59ff420dc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -1,12 +1,16 @@ using Microsoft.Search.Interop; using System; using System.Collections.Generic; -using System.Text; +using System.Data.OleDb; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { public class QueryConstructor { + public OleDbConnection conn; + public OleDbCommand command; + public OleDbDataReader dataReaderResults; + private Settings _settings; private const string SystemIndex = "SystemIndex"; @@ -18,14 +22,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex public CSearchQueryHelper CreateBaseQuery() { - // This uses the Microsoft.Search.Interop assembly - CSearchManager manager = new CSearchManager(); - - // SystemIndex catalog is the default catalog in Windows - ISearchCatalogManager catalogManager = manager.GetCatalog(SystemIndex); - - // Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer - var baseQuery = catalogManager.GetQueryHelper(); + var baseQuery = CreateQueryHelper(); // Set the number of results we want. Don't set this property if all results are needed. baseQuery.QueryMaxResults = _settings.MaxResult; @@ -33,7 +30,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex // Set list of columns we want to display, getting the path presently baseQuery.QuerySelectColumns = "System.FileName, System.ItemPathDisplay"; - // Filter based on folder/file name + // Filter based on file name baseQuery.QueryContentProperties = "System.FileName"; // Set sorting order @@ -42,6 +39,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return baseQuery; } + internal CSearchQueryHelper CreateQueryHelper() + { + // This uses the Microsoft.Search.Interop assembly + var manager = new CSearchManager(); + + // SystemIndex catalog is the default catalog in Windows + var catalogManager = manager.GetCatalog(SystemIndex); + + // Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer + var queryHelper = catalogManager.GetQueryHelper(); + + return queryHelper; + } + /// /// Set the required WHERE clause restriction to search on the first level of a specified directory. /// @@ -67,7 +78,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex /// public string QueryForAllFilesAndFolders(string userSearchString) { - // Generate SQL from our parameters, converting the userQuery from AQS->WHERE clause + // Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch(); } @@ -78,5 +89,40 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { return $"scope='file:'"; } + + internal List ExecuteWindowsIndexSearch(string query) + { + var results = new List(); + + using (conn = new OleDbConnection(CreateQueryHelper().ConnectionString)) + { + conn.Open(); + + using (command = new OleDbCommand(query, conn)) + { + // Results return as an OleDbDataReader. + using (dataReaderResults = command.ExecuteReader()) + { + if (dataReaderResults.HasRows) + { + while (dataReaderResults.Read()) + { + if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) + { + var result = new Result + { + Title = dataReaderResults.GetString(0), + SubTitle = dataReaderResults.GetString(1) + }; + results.Add(result); + } + } + } + } + } + } + + return results; + } } } From 7b69d98e5984d5a4cbabd9d73d5ebfa69e5be481 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 18 May 2020 22:23:20 +1000 Subject: [PATCH 023/142] Add Seacher class and move OleDb connection method --- .../Search/WindowsIndex/QueryConstructor.cs | 42 ------------- .../Search/WindowsIndex/Searcher.cs | 61 +++++++++++++++++++ 2 files changed, 61 insertions(+), 42 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index 59ff420dc..7a71c2519 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -1,16 +1,9 @@ using Microsoft.Search.Interop; -using System; -using System.Collections.Generic; -using System.Data.OleDb; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { public class QueryConstructor { - public OleDbConnection conn; - public OleDbCommand command; - public OleDbDataReader dataReaderResults; - private Settings _settings; private const string SystemIndex = "SystemIndex"; @@ -89,40 +82,5 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { return $"scope='file:'"; } - - internal List ExecuteWindowsIndexSearch(string query) - { - var results = new List(); - - using (conn = new OleDbConnection(CreateQueryHelper().ConnectionString)) - { - conn.Open(); - - using (command = new OleDbCommand(query, conn)) - { - // Results return as an OleDbDataReader. - using (dataReaderResults = command.ExecuteReader()) - { - if (dataReaderResults.HasRows) - { - while (dataReaderResults.Read()) - { - if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) - { - var result = new Result - { - Title = dataReaderResults.GetString(0), - SubTitle = dataReaderResults.GetString(1) - }; - results.Add(result); - } - } - } - } - } - } - - return results; - } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs new file mode 100644 index 000000000..3af30085d --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Data.OleDb; + +namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex +{ + internal class Searcher + { + public OleDbConnection conn; + + public OleDbCommand command; + + public OleDbDataReader dataReaderResults; + + private readonly object _lock = new object(); + + internal List ExecuteWindowsIndexSearch(string query, string connectionString) + { + var results = new List(); + + using (conn = new OleDbConnection(connectionString)) + { + conn.Open(); + + using (command = new OleDbCommand(query, conn)) + { + // Results return as an OleDbDataReader. + using (dataReaderResults = command.ExecuteReader()) + { + if (dataReaderResults.HasRows) + { + while (dataReaderResults.Read()) + { + if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) + { + var result = new Result + { + Title = dataReaderResults.GetString(0), + SubTitle = dataReaderResults.GetString(1) + }; + results.Add(result); + } + } + } + } + } + } + + return results; + } + + internal List WindowsIndexSearch(string query, string connectionString, Func constructQuery) + { + lock (_lock) + { + var constructedQuery = constructQuery(query); + return ExecuteWindowsIndexSearch(constructedQuery, connectionString); + } + } + } +} From d029c8d1a1b74d96f83b0d93ecf75ede59a2c373 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 18 May 2020 23:38:03 +1000 Subject: [PATCH 024/142] Update project properties --- .../Flow.Launcher.Plugin.Explorer.csproj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index b5a192c90..2b8fe291f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -4,18 +4,25 @@ Library netcoreapp3.1 true + false
- ..\..\Output\Debug\Plugins\Flow.Launcher.Explorer + ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Explorer - ..\..\Output\Release\Plugins\Flow.Launcher.Explorer + ..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Explorer + + + PreserveNewest + + + From f7d6c6f4b143e54aa2da7d1d569eb1d7e0d28667 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 18 May 2020 23:39:40 +1000 Subject: [PATCH 025/142] Update plugin descriptions --- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 4a6c8785c..35e8cc3ac 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -2,7 +2,7 @@ "ID": "572be03c74c642baae319fc283e561a8", "ActionKeyword": "*", "Name": "Explorer", - "Description": "Search and manage files, folders and subfolders. Explorer utilises Windows Index Search", + "Description": "Search for and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", "Version": "1.0.0", "Language": "csharp", From dd319c61b31db58454888e319ea5cee8f188fca0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 00:07:17 +1000 Subject: [PATCH 026/142] Update project file to include icon image --- .../Flow.Launcher.Plugin.Explorer.csproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 2b8fe291f..2de4a4149 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -23,6 +23,12 @@ + + + PreserveNewest + + + From be524cb6eac24d9437701815d4eff7fc49a2d76f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 00:11:37 +1000 Subject: [PATCH 027/142] Update Searcher class to handle result creation --- .../Search/WindowsIndex/Searcher.cs | 74 +++++++++++++++---- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs index 3af30085d..d36948079 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs @@ -1,6 +1,9 @@ -using System; +using Flow.Launcher.Infrastructure.Logger; +using System; using System.Collections.Generic; +using System.ComponentModel; using System.Data.OleDb; +using System.Diagnostics; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { @@ -11,44 +14,85 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex public OleDbCommand command; public OleDbDataReader dataReaderResults; + + private PluginInitContext _context; private readonly object _lock = new object(); + public Searcher(PluginInitContext context) + { + _context = context; + } + internal List ExecuteWindowsIndexSearch(string query, string connectionString) { var results = new List(); - using (conn = new OleDbConnection(connectionString)) + try { - conn.Open(); - - using (command = new OleDbCommand(query, conn)) + using (conn = new OleDbConnection(connectionString)) { - // Results return as an OleDbDataReader. - using (dataReaderResults = command.ExecuteReader()) + conn.Open(); + + using (command = new OleDbCommand(query, conn)) { - if (dataReaderResults.HasRows) + // Results return as an OleDbDataReader. + using (dataReaderResults = command.ExecuteReader()) { - while (dataReaderResults.Read()) + if (dataReaderResults.HasRows) { - if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) + while (dataReaderResults.Read()) { - var result = new Result + if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) { - Title = dataReaderResults.GetString(0), - SubTitle = dataReaderResults.GetString(1) - }; - results.Add(result); + results.Add(CreateResult(dataReaderResults)); + } } } } } } } + catch (InvalidOperationException) + { + // Internal error from ExecuteReader(): Connection closed. + } + catch (Exception ex) + { + Log.Info(ex.ToString());//UPDATE THIS LOGGING + } return results; } + private Result CreateResult(OleDbDataReader dataReaderResults) + { + return new Result + { + Title = dataReaderResults.GetString(0), + SubTitle = dataReaderResults.GetString(1), + IcoPath = "Images\\Explorer.png",//<------CHANGE + Action = c => + { + try + { + Process.Start(new ProcessStartInfo + { + FileName = dataReaderResults.GetString(1), + UseShellExecute = true, + //WorkingDirectory = workingDir <----?? + }); + } + catch (Win32Exception) + { + _context.API.ShowMsg("Explorer plugin: ", "Unable to open the selected file", string.Empty); //<========= + } + + return true; + } + }; + } + internal List WindowsIndexSearch(string query, string connectionString, Func constructQuery) { lock (_lock) From c295585272d6dca918f26a1bb103d8fc4bea3dd8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 00:14:41 +1000 Subject: [PATCH 028/142] Update Main to handle basic files and folder search query --- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 206911b6c..f4f63a3a6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -1,14 +1,16 @@ using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.Explorer.ViewModels; using Flow.Launcher.Plugin.Explorer.Views; using System; using System.Collections.Generic; using System.Text; +using System.Text.RegularExpressions; using System.Windows.Controls; namespace Flow.Launcher.Plugin.Explorer { - class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContextMenu + class Main : ISettingProvider, IPlugin, ISavable, IContextMenu //, IPluginI18n <=== do later { private PluginInitContext Context { get; set; } @@ -16,21 +18,14 @@ namespace Flow.Launcher.Plugin.Explorer private SettingsViewModel _viewModel; + // Reserved keywords in oleDB + private string ReservedStringPattern = @"^[\/\\\$\%]+$"; + public Control CreateSettingPanel() { return new ExplorerSettings(); } - public string GetTranslatedPluginDescription() - { - throw new NotImplementedException(); - } - - public string GetTranslatedPluginTitle() - { - throw new NotImplementedException(); - } - public void Init(PluginInitContext context) { Context = context; @@ -41,12 +36,28 @@ namespace Flow.Launcher.Plugin.Explorer public List LoadContextMenus(Result selectedResult) { - throw new NotImplementedException(); + return new List(); } public List Query(Query query) { - return new List(); + var results = new List(); + + if (string.IsNullOrEmpty(query.Search)) + return results; + + var regexMatch = Regex.Match(query.Search, ReservedStringPattern); + + if (regexMatch.Success) + return results; + + var queryConstructor = new QueryConstructor(_settings); + + var searcher = new Searcher(Context); + + return searcher.WindowsIndexSearch(query.Search, + queryConstructor.CreateQueryHelper().ConnectionString, + queryConstructor.QueryForAllFilesAndFolders); } public void Save() From 25ae9838e97eee508876365fcf2101e19032a450 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 00:15:30 +1000 Subject: [PATCH 029/142] Add packages for Windows Index Search to Plugin as required --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 82fbb31d5..4f1a34639 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -59,7 +59,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + \ No newline at end of file From 80380b230a56aa25ed4d622e0f91bdef9926e80a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 13:40:38 +1000 Subject: [PATCH 030/142] Change Search to open using shared command --- .../Search/WindowsIndex/Searcher.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs index d36948079..21067056d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs @@ -1,4 +1,5 @@ using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; using System.ComponentModel; @@ -45,7 +46,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) { - results.Add(CreateResult(dataReaderResults)); + results.Add(CreateResult(dataReaderResults.GetString(0), dataReaderResults.GetString(1))); } } } @@ -65,23 +66,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return results; } - private Result CreateResult(OleDbDataReader dataReaderResults) + private Result CreateResult(string filename, string path) { return new Result { - Title = dataReaderResults.GetString(0), - SubTitle = dataReaderResults.GetString(1), + Title = filename, + SubTitle = path, IcoPath = "Images\\Explorer.png",//<------CHANGE Action = c => { try { - Process.Start(new ProcessStartInfo - { - FileName = dataReaderResults.GetString(1), - UseShellExecute = true, - //WorkingDirectory = workingDir <----?? - }); + FilesFolders.OpenPath(path); } catch (Win32Exception) { From ac2d318412a0e0c0154e466ceb987c47794317bb Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 13:54:56 +1000 Subject: [PATCH 031/142] Rename Searcher class to IndexSearcher- more descriptive --- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 2 +- .../Search/WindowsIndex/{Searcher.cs => IndexSearcher.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/{Searcher.cs => IndexSearcher.cs} (97%) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index f4f63a3a6..1740e0e5b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -53,7 +53,7 @@ namespace Flow.Launcher.Plugin.Explorer var queryConstructor = new QueryConstructor(_settings); - var searcher = new Searcher(Context); + var searcher = new IndexSearcher(Context); return searcher.WindowsIndexSearch(query.Search, queryConstructor.CreateQueryHelper().ConnectionString, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs similarity index 97% rename from Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs rename to Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs index 21067056d..35b4a76e2 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/Searcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs @@ -8,7 +8,7 @@ using System.Diagnostics; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { - internal class Searcher + internal class IndexSearcher { public OleDbConnection conn; @@ -20,7 +20,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex private readonly object _lock = new object(); - public Searcher(PluginInitContext context) + public IndexSearcher(PluginInitContext context) { _context = context; } From 107fc75d9782aa3dbaf24b05bf99f130f8f83d02 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 18:46:42 +1000 Subject: [PATCH 032/142] Add file type icon images --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 4 ++-- .../Flow.Launcher.Plugin.Explorer.csproj | 16 ++++++++++++++++ .../Images/copy.png | Bin 0 -> 501 bytes .../Images/deletefilefolder.png | Bin 0 -> 19719 bytes .../Images/file.png | Bin 0 -> 290 bytes .../Images/folder.png | Bin 0 -> 468 bytes .../Search/Constants.cs | 16 ++++++++++++++++ .../Search/WindowsIndex/IndexSearcher.cs | 8 +++++--- .../Search/WindowsIndex/QueryConstructor.cs | 2 +- 9 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/copy.png create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/deletefilefolder.png create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/file.png create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/folder.png create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index f4028b27d..6f3c41aff 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -24,7 +24,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual: {result}{Environment.NewLine}"); } - [TestCase("C:\\Dropbox", "SELECT TOP 100 System.FileName, System.ItemPathDisplay FROM SystemIndex WHERE directory='file:C:\\Dropbox'")] + [TestCase("C:\\Dropbox", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\Dropbox'")] public void GivenWindowsIndexSearch_WhenSearchTypeIsSearchTopFolderLevel_ThenQueryShouldUseExpectedString(string folderPath, string expectedString) { // Given @@ -52,7 +52,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {resultString}{Environment.NewLine}"); } - [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\" " + + [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\", \"System.ItemType\" " + "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:'")] public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString( diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 2de4a4149..0261da36b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -27,6 +27,22 @@ PreserveNewest + + + PreserveNewest + + + + Always + + + + PreserveNewest + + + + PreserveNewest + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Images/copy.png b/Plugins/Flow.Launcher.Plugin.Explorer/Images/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..8f1fca752f87511c2a310b66f50f5eb60c2534fb GIT binary patch literal 501 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&3=E7#o-U3d z8I5me7foS%_Sik8XR}IS(n#wnctn%m-p4XMCcbM7wh&i5%%SE z-8`H{e3Op;`f_jS`**E(@_mX<{{3`*_jDJ@Q*qwfn^~(i zGtXgf)oq*?#=!70V`6&Db%q3yiamB8l@6*EtePs$c}?k?;=z4fh29mBZkDQhzB1}@ zw(j)`_;p}M|2E<5p2nMhuNJz=sxK0L?a~-D;hedvo2<6egJ)|V>nJ)osGo{-YGG*D z&9d^>y=%AQxA*^cK9w1u)p`A{`je%e&u26J)6OyE3<;Plu(8KaZ_?h}%FIG49IFI z5`TSR9m|fa1%6R)y%er7Fn`^DOfX4)LyK->1oN~7oNJcd-+v*e;+|M_eEvrM2%&5R plRz+fF@NW>3qM#EIb`f(n0vqFn0I?{Bru8?JYD@<);T3K0RYUm%ozXx literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Images/deletefilefolder.png b/Plugins/Flow.Launcher.Plugin.Explorer/Images/deletefilefolder.png new file mode 100644 index 0000000000000000000000000000000000000000..024cc92915c48cba2f706ba344cba0c8d8bcbce5 GIT binary patch literal 19719 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 a8omH#B09jfr)wU0a`eba`zl-C`2Pd0VS!Zu literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Images/file.png b/Plugins/Flow.Launcher.Plugin.Explorer/Images/file.png new file mode 100644 index 0000000000000000000000000000000000000000..36156767a6572d31205104d16a8e909c5af378b4 GIT binary patch literal 290 zcmV+-0p0$IP)=FA?pM#)7Nq6>`@rGF3#p%4mjka3Wys(?018&wG;f~}U&3=E8co-U3d z8I5me*m@mtkZ_2f6nK;6kLH<03QBPa#m%|8dmGy~l)trY&e*zi=~{MyEqCH>uzqLg z-ee)R(a_j+)5M=XYzIoGgL`n3r5+$TV?~ zLSY^gH{i|-ls|r!cY{r+O=Pm>F}@8wk^OH?4jUc2 v!FV(!YHrE@-m;$hpQ5isz&^hGvmt+Oh5d!HHwsq37+~;p^>bP0l+XkKMAE;S literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs new file mode 100644 index 000000000..2be1dfd7a --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Flow.Launcher.Plugin.Explorer.Search +{ + public static class Constants + { + public const string FolderImagePath = "Images\\folder.png"; + public const string FileImagePath = "Images\\file.png"; + public const string DeleteFileFolderImagePath = "Images\\deletefilefolder.png"; + public const string CopyImagePath = "Images\\copy.png"; + + public const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory"; + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs index 35b4a76e2..022a70cdf 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs @@ -46,7 +46,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) { - results.Add(CreateResult(dataReaderResults.GetString(0), dataReaderResults.GetString(1))); + results.Add(CreateResult(dataReaderResults.GetString(0), + dataReaderResults.GetString(1), + dataReaderResults.GetString(2))); } } } @@ -66,13 +68,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return results; } - private Result CreateResult(string filename, string path) + private Result CreateResult(string filename, string path, string fileType) { return new Result { Title = filename, SubTitle = path, - IcoPath = "Images\\Explorer.png",//<------CHANGE + IcoPath = fileType == "Directory" ? Constants.FolderImagePath : Constants.FileImagePath, Action = c => { try diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index 7a71c2519..d74a11b48 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex baseQuery.QueryMaxResults = _settings.MaxResult; // Set list of columns we want to display, getting the path presently - baseQuery.QuerySelectColumns = "System.FileName, System.ItemPathDisplay"; + baseQuery.QuerySelectColumns = "System.FileName, System.ItemPathDisplay, System.ItemType"; // Filter based on file name baseQuery.QueryContentProperties = "System.FileName"; From 06667b7aa76e4f81ba18d8a62225f7a279b5c49f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 20:10:46 +1000 Subject: [PATCH 033/142] Add tests for TopLevelFolderSearch behaviour --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 69 ++++++++++++++++++- .../Search/SearchManager.cs | 23 +++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 6f3c41aff..780feae3f 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -1,13 +1,41 @@ +using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.Explorer; +using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using NUnit.Framework; using System; +using System.Collections.Generic; namespace Flow.Launcher.Test.Plugins { [TestFixture] public class ExplorerTest { + private List MethodWindowsIndexSearchReturnsZeroResults(string dummyString) + { + return new List(); + } + + private List MethodDirectoryInfoClassSearchReturnsTwoResults(string dummyString) + { + return new List + { + new Result + { + Title="Result 1" + }, + + new Result + { + Title="Result 2" + } + }; + } + + private bool MethodIndexExistsReturnsTrue(string dummyString) => true; + + private bool MethodIndexExistsReturnsFalse(string dummyString) => false; + [TestCase("C:\\Dropbox", "directory='file:C:\\Dropbox'")] public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString) { @@ -33,6 +61,7 @@ namespace Flow.Launcher.Test.Plugins //When var queryString = queryConstructor.QueryForTopLevelDirectorySearch(folderPath); + // Then Assert.IsTrue(queryString == expectedString, $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + $"Actual string was: {queryString}{Environment.NewLine}"); @@ -47,6 +76,7 @@ namespace Flow.Launcher.Test.Plugins //When var resultString = queryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch(); + // Then Assert.IsTrue(resultString == expectedString, $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + $"Actual string was: {resultString}{Environment.NewLine}"); @@ -64,12 +94,49 @@ namespace Flow.Launcher.Test.Plugins //When var resultString = queryConstructor.QueryForAllFilesAndFolders(userSearchString); + // Then Assert.IsTrue(resultString == expectedString, $"Expected query string: {expectedString}{Environment.NewLine} " + $"Actual string was: {resultString}{Environment.NewLine}"); } - public void GivenWindowsIndexSearch_WhenReturnedNilAndIsNotIndexed_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() { } + [TestCase] + public void GivenWindowsIndexSearch_WhenReturnedZeroResultsAndIsNotIndexed_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() + { + // Given + var searchManager = new SearchManager(); + + // When + var results = searchManager.TopLevelFolderSearch( + MethodWindowsIndexSearchReturnsZeroResults, + MethodDirectoryInfoClassSearchReturnsTwoResults, + MethodIndexExistsReturnsFalse, + "path string not used"); + + // Then + Assert.IsTrue(results.Count == 2, + $"Expected to have 2 results from DirectoryInfoClassSearch {Environment.NewLine} " + + $"Actual number of results is {results.Count} {Environment.NewLine}"); + } + + [TestCase] + public void GivenWindowsIndexSearch_WhenReturnedZeroResultsAndIsIndexed_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch() + { + // Given + var searchManager = new SearchManager(); + + // When + var results = searchManager.TopLevelFolderSearch( + MethodWindowsIndexSearchReturnsZeroResults, + MethodDirectoryInfoClassSearchReturnsTwoResults, + MethodIndexExistsReturnsTrue, + "path string not used"); + + // Then + Assert.IsTrue(results.Count == 0, + $"Expected to have 0 results because location is indexed {Environment.NewLine} " + + $"Actual number of results is {results.Count} {Environment.NewLine}"); + } public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString() { } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs new file mode 100644 index 000000000..c2a250ae7 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Flow.Launcher.Plugin.Explorer.Search +{ + public class SearchManager + { + public List TopLevelFolderSearch( + Func> windowsIndexSearch, + Func> directoryInfoClassSearch, + Func indexExists, + string path) + { + var results = windowsIndexSearch(path); + + if (results.Count == 0 && !indexExists(path)) + return directoryInfoClassSearch(path); + + return results; + } + } +} From 78b797e43b8f011c932139f634a7d48b890bd454 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 22:36:56 +1000 Subject: [PATCH 034/142] Add tests for checking string is location path --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 24 +++++++++- .../Search/SearchManager.cs | 46 ++++++++++++++++++- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 780feae3f..38090572e 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -104,7 +104,7 @@ namespace Flow.Launcher.Test.Plugins public void GivenWindowsIndexSearch_WhenReturnedZeroResultsAndIsNotIndexed_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() { // Given - var searchManager = new SearchManager(); + var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When var results = searchManager.TopLevelFolderSearch( @@ -123,7 +123,7 @@ namespace Flow.Launcher.Test.Plugins public void GivenWindowsIndexSearch_WhenReturnedZeroResultsAndIsIndexed_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch() { // Given - var searchManager = new SearchManager(); + var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When var results = searchManager.TopLevelFolderSearch( @@ -139,5 +139,25 @@ namespace Flow.Launcher.Test.Plugins } public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString() { } + + [TestCase(@"c:\\", false)] + [TestCase(@"i:\", true)] + [TestCase(@"\c:\", false)] + [TestCase(@"cc:\", false)] + [TestCase(@"\\\SomeNetworkLocation\", false)] + [TestCase("RandomString", false)] + public void WhenGivenQuerySearchString_ThenShouldIndicateIfItIsLocationString(string querySearchString, bool expectedResult) + { + // When, Given + var searchManager = new SearchManager(new Settings(), new PluginInitContext()); + + var result = searchManager.IsLocationPathString(querySearchString); + + //Then + Assert.IsTrue(result == expectedResult, + $"Expected query search string check result is: {expectedResult} {Environment.NewLine} " + + $"Actual check result is {result} {Environment.NewLine}"); + + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index c2a250ae7..1dd834428 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,11 +1,53 @@ -using System; +using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using System; using System.Collections.Generic; -using System.Text; namespace Flow.Launcher.Plugin.Explorer.Search { public class SearchManager { + private Settings _settings; + private PluginInitContext _context; + + public SearchManager(Settings settings, PluginInitContext context) + { + _settings = settings; + _context = context; + } + + /// + /// This checks whether a given string is a directory path or network location string. + /// It does not check if location actually exists. + /// + public bool IsLocationPathString(string querySearchString) + { + if (string.IsNullOrEmpty(querySearchString)) + return false; + + // // shared folder location, and not \\\location\ + if (querySearchString.Length >= 3 + && querySearchString.StartsWith(@"\\") + && char.IsLetter(querySearchString[2])) + return true; + + // c:\ + if (querySearchString.Length == 3 + && char.IsLetter(querySearchString[0]) + && querySearchString[1] == ':' + && querySearchString[2] == '\\') + return true; + + // c:\\ + if (querySearchString.Length >= 4 + && char.IsLetter(querySearchString[0]) + && querySearchString[1] == ':' + && querySearchString[2] == '\\' + && char.IsLetter(querySearchString[3])) + return true; + + return false; + } + public List TopLevelFolderSearch( Func> windowsIndexSearch, Func> directoryInfoClassSearch, From 690d60b7011135f40c852ad2aa0d335e66abf7a8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 22:38:42 +1000 Subject: [PATCH 035/142] Add the use of a SearchManager --- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 12 ++---------- .../Search/SearchManager.cs | 11 +++++++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 1740e0e5b..80f872cf6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -1,10 +1,8 @@ using Flow.Launcher.Infrastructure.Storage; -using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.ViewModels; using Flow.Launcher.Plugin.Explorer.Views; -using System; using System.Collections.Generic; -using System.Text; using System.Text.RegularExpressions; using System.Windows.Controls; @@ -51,13 +49,7 @@ namespace Flow.Launcher.Plugin.Explorer if (regexMatch.Success) return results; - var queryConstructor = new QueryConstructor(_settings); - - var searcher = new IndexSearcher(Context); - - return searcher.WindowsIndexSearch(query.Search, - queryConstructor.CreateQueryHelper().ConnectionString, - queryConstructor.QueryForAllFilesAndFolders); + return new SearchManager(_settings, Context).Search(query.Search); } public void Save() diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 1dd834428..80a764d50 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -61,5 +61,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search return results; } + + internal List WindowsIndexFilesAndFoldersSearch(string querySearchString) + { + var queryConstructor = new QueryConstructor(_settings); + + var searcher = new IndexSearcher(_context); + + return searcher.WindowsIndexSearch(querySearchString, + queryConstructor.CreateQueryHelper().ConnectionString, + queryConstructor.QueryForAllFilesAndFolders); + } } } From bf6a9d616a315982166518a229b241efaeb4e7af Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 May 2020 22:40:27 +1000 Subject: [PATCH 036/142] Rename parameters in IndexSearcher --- .../Search/WindowsIndex/IndexSearcher.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs index 022a70cdf..837c316b0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; @@ -25,7 +25,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex _context = context; } - internal List ExecuteWindowsIndexSearch(string query, string connectionString) + internal List ExecuteWindowsIndexSearch(string searchString, string connectionString) { var results = new List(); @@ -35,7 +35,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { conn.Open(); - using (command = new OleDbCommand(query, conn)) + using (command = new OleDbCommand(searchString, conn)) { // Results return as an OleDbDataReader. using (dataReaderResults = command.ExecuteReader()) @@ -46,9 +46,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) { - results.Add(CreateResult(dataReaderResults.GetString(0), - dataReaderResults.GetString(1), - dataReaderResults.GetString(2))); + results.Add(CreateResult( + dataReaderResults.GetString(0), + dataReaderResults.GetString(1), + dataReaderResults.GetString(2))); } } } @@ -91,11 +92,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex }; } - internal List WindowsIndexSearch(string query, string connectionString, Func constructQuery) + internal List WindowsIndexSearch(string searchString, string connectionString, Func constructQuery) { lock (_lock) { - var constructedQuery = constructQuery(query); + var constructedQuery = constructQuery(searchString); return ExecuteWindowsIndexSearch(constructedQuery, connectionString); } } From 960593eb58f3a6cecfe032e3dd8b97c79f1a7f14 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 21 May 2020 07:48:52 +1000 Subject: [PATCH 037/142] Add handling of reflection type error --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 35486e794..c6c357de0 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -53,10 +53,12 @@ namespace Flow.Launcher.Core.Plugin Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for {metadata.Name}", e); return; } - var types = assembly.GetTypes(); + Type type; try { + var types = assembly.GetTypes(); + type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); } catch (InvalidOperationException e) @@ -64,6 +66,12 @@ namespace Flow.Launcher.Core.Plugin Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find class implement IPlugin for <{metadata.Name}>", e); return; } + catch (ReflectionTypeLoadException e) + { + Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for <{metadata.Name}>", e); + return; + } + IPlugin plugin; try { From ecbcc5923f22766d0b7e73d070451b26c92eb75f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 21 May 2020 08:47:09 +1000 Subject: [PATCH 038/142] Add error message to inform user of failed plugins --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index c6c357de0..1f6a17573 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -29,6 +29,8 @@ namespace Flow.Launcher.Core.Plugin public static IEnumerable DotNetPlugins(List source) { + var erroredPlugins = new List(); + var plugins = new List(); var metadatas = source.Where(o => AllowedLanguage.IsDotNet(o.Language)); @@ -50,6 +52,8 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { + erroredPlugins.Add(metadata.Name); + Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for {metadata.Name}", e); return; } @@ -63,11 +67,15 @@ namespace Flow.Launcher.Core.Plugin } catch (InvalidOperationException e) { + erroredPlugins.Add(metadata.Name); + Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find class implement IPlugin for <{metadata.Name}>", e); return; } catch (ReflectionTypeLoadException e) { + erroredPlugins.Add(metadata.Name); + Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for <{metadata.Name}>", e); return; } @@ -79,6 +87,7 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { + erroredPlugins.Add(metadata.Name); Log.Exception($"|PluginsLoader.DotNetPlugins|Can't create instance for <{metadata.Name}>", e); return; } @@ -93,6 +102,19 @@ namespace Flow.Launcher.Core.Plugin metadata.InitTime += milliseconds; } + + if (erroredPlugins.Count > 0) + { + var errorPluginString = ""; + + erroredPlugins.ForEach(x => errorPluginString += x + Environment.NewLine); + + Task.Run(() => + { + MessageBox.Show($"Uh oh, unable to load plugins:{Environment.NewLine}{errorPluginString}"); + }); + } + return plugins; } From 9bddbe84553e81fbd7b4c07f33d534277e7d92f1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 21 May 2020 09:02:51 +1000 Subject: [PATCH 039/142] Add references --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 1f6a17573..b1cb8d08d 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -4,8 +4,9 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Loader; +using System.Threading.Tasks; +using System.Windows; using Flow.Launcher.Infrastructure; -using Flow.Launcher.Infrastructure.Exception; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; From 3a472a34e4789dbd750b5fa4291799a358714913 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 21 May 2020 09:04:28 +1000 Subject: [PATCH 040/142] update --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index b1cb8d08d..51fa5bc9b 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -89,6 +89,7 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { erroredPlugins.Add(metadata.Name); + Log.Exception($"|PluginsLoader.DotNetPlugins|Can't create instance for <{metadata.Name}>", e); return; } From 04b48eb6163825a778c83231fd6f8fe54dbf5101 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Thu, 21 May 2020 03:08:02 +0300 Subject: [PATCH 041/142] downgrade PropertyChanged.Fody to 2.5.13 This is the latest version of the package that copies itself to the build output. Downgrading fixes runtime errors. --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 5 +---- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 5 +---- Flow.Launcher/Flow.Launcher.csproj | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index fa3f10fa7..87c390d34 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -57,10 +57,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 7965f75c6..8a2e45565 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -73,10 +73,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 679cf22a3..b63a204e7 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -61,10 +61,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + From 63478933568396d39809722d804d73d67ba1a797 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Thu, 21 May 2020 03:10:05 +0300 Subject: [PATCH 042/142] appveyor: ignore squirrel artifacts The squirrel artifats produced (nupkg, RELEASES and exe) are not ready for deployment as they depend on the .net core runtime being installed. We remove them temporarily from the build artifacts to avoid confusion, until this issue has been addressed. --- appveyor.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 243e3ed2c..fb13b0796 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,12 +34,6 @@ build: verbosity: minimal artifacts: -- path: 'Output\Packages\*.exe' - name: Setup File -- path: 'Output\Packages\*.nupkg' - name: Squirrel nupkg -- path: 'Output\Packages\RELEASES' - name: Squirrel RELEASES - path: 'Output\Packages\Flow-Launcher-*.zip' name: Zip - path: 'Output\Release\Flow.Launcher.Plugin.*.nupkg' From 408fabd5563888d5153ce05daae4ef62657af793 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Thu, 21 May 2020 03:16:47 +0300 Subject: [PATCH 043/142] update project versions --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 4 ++-- appveyor.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 8a2e45565..c353fd483 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -15,7 +15,7 @@ 1.0.0 - 1.0.0 + 1.0.0-beta1 1.0.0 1.0.0 Flow.Launcher.Plugin diff --git a/appveyor.yml b/appveyor.yml index fb13b0796..cd0e27918 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.0.0.{build}' +version: '0.9.0.{build}' init: - ps: | From 945c124b3182e244ae2e12d8f52c6a0c116ab19f Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Thu, 21 May 2020 03:32:55 +0300 Subject: [PATCH 044/142] appveyor: do not patch csproj files The way the project files are currently configured, this setting resulted in only the Plugin project being patched. Consequently, the version of the Plugin project was wrong in the built package. --- appveyor.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index cd0e27918..02027f83e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,14 +11,6 @@ assembly_info: assembly_version: $(flowVersion) assembly_file_version: $(flowVersion) assembly_informational_version: $(flowVersion) -dotnet_csproj: - patch: true - file: 'Flow.*\*.csproj' - version: $(flowVersion) - package_version: $(flowVersion) - assembly_version: $(flowVersion) - file_version: $(flowVersion) - informational_version: $(flowVersion) skip_commits: files: From f98eee6058ed26b4cdfd0420c0aab263c4e57e89 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 21 May 2020 13:31:24 +1000 Subject: [PATCH 045/142] Fix nuget commandline reference --- Flow.Launcher/Flow.Launcher.csproj | 4 ++++ Scripts/post_build.ps1 | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 568b6d481..970bc8e37 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -70,6 +70,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 16f27476f..624f4f502 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -125,6 +125,8 @@ function Main { Delete-Unused $p $config $o = "$p\Output\Packages" Validate-Directory $o + # making version static as multiple versions can exist in the nuget folder and in the case a breaking change is introduced. + New-Alias Nuget $env:USERPROFILE\.nuget\packages\NuGet.CommandLine\5.4.0\tools\NuGet.exe -Force Pack-Squirrel-Installer $p $v $o $isInCI = $env:APPVEYOR From 72640f62d742fca1488f1a03ec1d6e0becbd33bc Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Thu, 21 May 2020 21:57:03 +0300 Subject: [PATCH 046/142] plugin: in release, create a portable pdb file The nuget symbols server only supports portable pdbs. This results in "Symbols package publishing failed." when uploading the .snupkg file --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index c353fd483..614d0ed43 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -43,7 +43,7 @@ - pdbonly + portable true ..\Output\Release\ TRACE From 8171df9995598392167884437ae55944d8eaea38 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Thu, 21 May 2020 23:39:44 +0300 Subject: [PATCH 047/142] plugin: bump version to 1.0.0-beta2 --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 614d0ed43..3cbe35d6d 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -15,7 +15,7 @@ 1.0.0 - 1.0.0-beta1 + 1.0.0-beta2 1.0.0 1.0.0 Flow.Launcher.Plugin From 9058afd1b0f381dcb7452f9fd0aab1feebdcf1b7 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 22 May 2020 06:48:03 +1000 Subject: [PATCH 048/142] Update error messages --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 51fa5bc9b..afbc18fdb 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -55,7 +55,7 @@ namespace Flow.Launcher.Core.Plugin { erroredPlugins.Add(metadata.Name); - Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for {metadata.Name}", e); + Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e); return; } @@ -70,14 +70,14 @@ namespace Flow.Launcher.Core.Plugin { erroredPlugins.Add(metadata.Name); - Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find class implement IPlugin for <{metadata.Name}>", e); + Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e); return; } catch (ReflectionTypeLoadException e) { erroredPlugins.Add(metadata.Name); - Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for <{metadata.Name}>", e); + Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e); return; } @@ -90,7 +90,7 @@ namespace Flow.Launcher.Core.Plugin { erroredPlugins.Add(metadata.Name); - Log.Exception($"|PluginsLoader.DotNetPlugins|Can't create instance for <{metadata.Name}>", e); + Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e); return; } #endif @@ -109,11 +109,15 @@ namespace Flow.Launcher.Core.Plugin { var errorPluginString = ""; + var errorMessage = "The following " + + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") + + "errored and cannot be loaded:"; + erroredPlugins.ForEach(x => errorPluginString += x + Environment.NewLine); Task.Run(() => { - MessageBox.Show($"Uh oh, unable to load plugins:{Environment.NewLine}{errorPluginString}"); + MessageBox.Show($"{errorMessage}{Environment.NewLine}{errorPluginString}"); }); } From acdca571fa4d65a7a4e67e2e31edbf7564f0e162 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 23 May 2020 14:58:57 +1000 Subject: [PATCH 049/142] Update ActionKeyword window --- Flow.Launcher/ActionKeywords.xaml | 32 +++++++++++++++---------------- Flow.Launcher/Languages/en.xaml | 3 ++- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Flow.Launcher/ActionKeywords.xaml b/Flow.Launcher/ActionKeywords.xaml index 39574964b..38b987a5e 100644 --- a/Flow.Launcher/ActionKeywords.xaml +++ b/Flow.Launcher/ActionKeywords.xaml @@ -1,4 +1,4 @@ - + Height="250" Width="500"> - + - + - - - Old ActionKeywords: - - - + + + + - + - - diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 1b69c1b90..4eaed2396 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -40,7 +40,8 @@ Plugin Find more plugins Disable - Action keywords + Current action keyword: + New action keyword: Plugin Directory Author Init time: {0}ms From 66334947e2a82ec20238fe736a2700679375b932 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 23 May 2020 16:37:29 +1000 Subject: [PATCH 050/142] TopLevelFolderSearch- Change name to reflect behaviour --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 38090572e..4ca665cab 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -107,7 +107,7 @@ namespace Flow.Launcher.Test.Plugins var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When - var results = searchManager.TopLevelFolderSearch( + var results = searchManager.TopLevelFolderSearchBehaviour( MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, MethodIndexExistsReturnsFalse, @@ -126,7 +126,7 @@ namespace Flow.Launcher.Test.Plugins var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When - var results = searchManager.TopLevelFolderSearch( + var results = searchManager.TopLevelFolderSearchBehaviour( MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, MethodIndexExistsReturnsTrue, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 80a764d50..6d74775cc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -48,7 +48,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return false; } - public List TopLevelFolderSearch( + public List TopLevelFolderSearchBehaviour( Func> windowsIndexSearch, Func> directoryInfoClassSearch, Func indexExists, From 903984807834ec78ab0508aeaab6a5448c529b88 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 23 May 2020 16:40:31 +1000 Subject: [PATCH 051/142] Add method in search manager to handle top folder level win index search --- .../Search/SearchManager.cs | 41 +++++++++++++++++-- .../Search/WindowsIndex/IndexSearcher.cs | 11 ++++- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 6d74775cc..6e8a61095 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using System; using System.Collections.Generic; @@ -9,10 +9,31 @@ namespace Flow.Launcher.Plugin.Explorer.Search private Settings _settings; private PluginInitContext _context; + private IndexSearcher searcher; + public SearchManager(Settings settings, PluginInitContext context) { _settings = settings; _context = context; + searcher = new IndexSearcher(_context); + } + + public List Search(string querySearchString) + { + if (IsLocationPathString(querySearchString)) + { + return TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, + DirectoryInfoClassSearch, + WindowsIndexExists, + querySearchString); + } + + return WindowsIndexFilesAndFoldersSearch(querySearchString); + } + + private List DirectoryInfoClassSearch(string arg) + { + throw new NotImplementedException(); } /// @@ -62,15 +83,27 @@ namespace Flow.Launcher.Plugin.Explorer.Search return results; } - internal List WindowsIndexFilesAndFoldersSearch(string querySearchString) + private List WindowsIndexFilesAndFoldersSearch(string querySearchString) { var queryConstructor = new QueryConstructor(_settings); - var searcher = new IndexSearcher(_context); - return searcher.WindowsIndexSearch(querySearchString, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForAllFilesAndFolders); } + + private List WindowsIndexTopLevelFolderSearch(string path) + { + var queryConstructor = new QueryConstructor(_settings); + + return searcher.WindowsIndexSearch(path, + queryConstructor.CreateQueryHelper().ConnectionString, + queryConstructor.QueryForTopLevelDirectorySearch); + } + + private bool WindowsIndexExists(string path) + { + return searcher.PathIsIndexed(path); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs index 837c316b0..56ed8ddc6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs @@ -1,5 +1,6 @@ -using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; +using Microsoft.Search.Interop; using System; using System.Collections.Generic; using System.ComponentModel; @@ -100,5 +101,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return ExecuteWindowsIndexSearch(constructedQuery, connectionString); } } + + internal bool PathIsIndexed(string path) + { + var csm = new CSearchManager(); + var indexManager = csm.GetCatalog("SystemIndex").GetCrawlScopeManager(); + + return indexManager.IncludedInCrawlScope(path) == 0; + } } } From 9b416bf75fd8f96a821552fb28b07b7ee656a23a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 May 2020 19:09:44 +1000 Subject: [PATCH 052/142] Add EnvironmentVariablesPath handling --- .../Search/EnvironmentVariables.cs | 90 +++++++++++++++++++ .../Search/SearchManager.cs | 16 +++- 2 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs new file mode 100644 index 000000000..dc48512aa --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Flow.Launcher.Plugin.Explorer.Search +{ + public static class EnvironmentVariables + { + internal static bool IsEnvironmentVariableSearch(string search) + { + return LoadEnvironmentStringPaths().Count > 0 && search.StartsWith("%") && !search.Substring(1).Contains("%"); + } + + internal static Dictionary LoadEnvironmentStringPaths() + { + var envStringPaths = new Dictionary(); + + foreach (DictionaryEntry special in Environment.GetEnvironmentVariables()) + { + if (Directory.Exists(special.Value.ToString())) + { + envStringPaths.Add(special.Key.ToString().ToLower(), special.Value.ToString()); + } + } + + return envStringPaths; + } + + internal static string TranslateEnvironmentVariablePath(string environmentVariablePath) + { + var envStringPaths = LoadEnvironmentStringPaths(); + var splitSearch = environmentVariablePath.Substring(1).Split("%"); + var exactEnvStringPath = splitSearch[0]; + + // if there are more than 2 % characters in the query, don't bother + if (splitSearch.Length == 2 && envStringPaths.ContainsKey(exactEnvStringPath)) + { + var queryPartToReplace = $"%{exactEnvStringPath}%"; + var expandedPath = envStringPaths[exactEnvStringPath]; + // replace the %envstring% part of the query with its expanded equivalent + return environmentVariablePath.Replace(queryPartToReplace, expandedPath); + } + + return environmentVariablePath; + } + + internal static List GetEnvironmentStringPathSuggestions(string querySearch, Query query) + { + var results = new List(); + + var environmentVariables = LoadEnvironmentStringPaths(); + var search = querySearch; + + if (querySearch.EndsWith("%") && search.Length > 1) + { + // query starts and ends with a %, find an exact match from env-string paths + search = querySearch.Substring(1, search.Length - 2); + + if (environmentVariables.ContainsKey(search)) + { + var expandedPath = environmentVariables[search]; + + results.Add(new ResultManager().CreateFolderResult($"%{search}%", expandedPath, expandedPath, query)); + + return results; + } + } + + if (querySearch == "%") + { + search = ""; // Get all paths + } + else + { + search = search.Substring(1); + } + + foreach (var p in environmentVariables) + { + if (p.Key.StartsWith(search)) + { + results.Add(new ResultManager().CreateFolderResult($"%{p.Key}%", p.Value, p.Value, query)); + } + } + return results; + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 6e8a61095..e2e195f4d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using System; using System.Collections.Generic; @@ -20,8 +20,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search public List Search(string querySearchString) { - if (IsLocationPathString(querySearchString)) + var querySearch = query.Search; + + if (EnvironmentVariables.IsEnvironmentVariableSearch(querySearch)) { + return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query); + } + + // Query is a location path with a full environment variable- starts with a % + // and contains another % somewhere before the end of the path + if (querySearch.Substring(1).Contains("%")) + { + querySearch = EnvironmentVariables.TranslateEnvironmentVariablePath(querySearch); + } + return TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, DirectoryInfoClassSearch, WindowsIndexExists, From 191efe31cbc24a1753e2d9fef5b3ef420ad57bd3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 May 2020 19:11:40 +1000 Subject: [PATCH 053/142] Add ResultManager for handling creation of folder and file result --- .../Search/ResultManager.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs new file mode 100644 index 000000000..3ee7848d8 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -0,0 +1,59 @@ +using Flow.Launcher.Infrastructure; +using Flow.Launcher.Plugin.SharedCommands; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; + +namespace Flow.Launcher.Plugin.Explorer.Search +{ + public class ResultManager + { + public Result CreateFolderResult(string title, string subtitle, string path, Query query) + { + return new Result + { + Title = title, + IcoPath = path, + SubTitle = subtitle, + TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData, + Action = c => + { + if (c.SpecialKeyState.CtrlPressed) + { + try + { + FilesFolders.OpenPath(path); + return true; + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Could not start " + path); + return false; + } + } + + string changeTo = path.EndsWith("\\") ? path : path + "\\"; + Main.Context.API.ChangeQuery(string.IsNullOrEmpty(query.ActionKeyword) ? + changeTo : + query.ActionKeyword + " " + changeTo); + return false; + }, + ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path } + }; + } + } + + public class SearchResult + { + public string FullPath { get; set; } + public ResultType Type { get; set; } + } + + public enum ResultType + { + Volume, + Folder, + File + } +} From 472f4a5f49308a873749f794f6e121ba29a1c4a8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 May 2020 19:15:13 +1000 Subject: [PATCH 054/142] Move ReservedStringPattern to IndexSearcher class --- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 10 ++-------- .../Search/WindowsIndex/IndexSearcher.cs | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 80f872cf6..e5c9e8e2f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.ViewModels; using Flow.Launcher.Plugin.Explorer.Views; @@ -16,9 +16,6 @@ namespace Flow.Launcher.Plugin.Explorer private SettingsViewModel _viewModel; - // Reserved keywords in oleDB - private string ReservedStringPattern = @"^[\/\\\$\%]+$"; - public Control CreateSettingPanel() { return new ExplorerSettings(); @@ -44,10 +41,7 @@ namespace Flow.Launcher.Plugin.Explorer if (string.IsNullOrEmpty(query.Search)) return results; - var regexMatch = Regex.Match(query.Search, ReservedStringPattern); - - if (regexMatch.Success) - return results; + return new SearchManager(_settings, Context).Search(query); return new SearchManager(_settings, Context).Search(query.Search); } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs index 56ed8ddc6..9f99a6248 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; using Microsoft.Search.Interop; using System; @@ -6,11 +6,14 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data.OleDb; using System.Diagnostics; +using System.Text.RegularExpressions; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { internal class IndexSearcher { + private readonly object _lock = new object(); + public OleDbConnection conn; public OleDbCommand command; @@ -18,8 +21,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex public OleDbDataReader dataReaderResults; private PluginInitContext _context; - - private readonly object _lock = new object(); + + // Reserved keywords in oleDB + private string ReservedStringPattern = @"^[\/\\\$\%]+$"; public IndexSearcher(PluginInitContext context) { @@ -95,6 +99,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex internal List WindowsIndexSearch(string searchString, string connectionString, Func constructQuery) { + var regexMatch = Regex.Match(searchString, ReservedStringPattern); + + if (regexMatch.Success) + return new List(); + lock (_lock) { var constructedQuery = constructQuery(searchString); From df0871a924d64fe52aa9d45c03f605177bba3a22 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 May 2020 19:16:10 +1000 Subject: [PATCH 055/142] Fix incorrect return state on PathIsIndexed --- .../Search/WindowsIndex/IndexSearcher.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs index 9f99a6248..6081bf119 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; using Microsoft.Search.Interop; using System; @@ -115,8 +115,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { var csm = new CSearchManager(); var indexManager = csm.GetCatalog("SystemIndex").GetCrawlScopeManager(); - - return indexManager.IncludedInCrawlScope(path) == 0; + return indexManager.IncludedInCrawlScope(path) > 0; } } } From c5c15d2ab398173664d9b1c62c01640e46ead543 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 May 2020 19:18:58 +1000 Subject: [PATCH 056/142] Move IsLocationPathString method to FilesFolder class --- .../SharedCommands/FilesFolders.cs | 33 +++++++++++++++++++ .../Search/SearchManager.cs | 30 ----------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 17c2dc511..440ac6756 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -127,5 +127,38 @@ namespace Flow.Launcher.Plugin.SharedCommands #endif } } + + /// + /// This checks whether a given string is a directory path or network location string. + /// It does not check if location actually exists. + /// + public static bool IsLocationPathString(string querySearchString) + { + if (string.IsNullOrEmpty(querySearchString)) + return false; + + // // shared folder location, and not \\\location\ + if (querySearchString.Length >= 3 + && querySearchString.StartsWith(@"\\") + && char.IsLetter(querySearchString[2])) + return true; + + // c:\ + if (querySearchString.Length == 3 + && char.IsLetter(querySearchString[0]) + && querySearchString[1] == ':' + && querySearchString[2] == '\\') + return true; + + // c:\\ + if (querySearchString.Length >= 4 + && char.IsLetter(querySearchString[0]) + && querySearchString[1] == ':' + && querySearchString[2] == '\\' + && char.IsLetter(querySearchString[3])) + return true; + + return false; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index e2e195f4d..1f2327e5c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -49,36 +49,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search } /// - /// This checks whether a given string is a directory path or network location string. - /// It does not check if location actually exists. - /// - public bool IsLocationPathString(string querySearchString) - { - if (string.IsNullOrEmpty(querySearchString)) - return false; - - // // shared folder location, and not \\\location\ - if (querySearchString.Length >= 3 - && querySearchString.StartsWith(@"\\") - && char.IsLetter(querySearchString[2])) - return true; - - // c:\ - if (querySearchString.Length == 3 - && char.IsLetter(querySearchString[0]) - && querySearchString[1] == ':' - && querySearchString[2] == '\\') - return true; - - // c:\\ - if (querySearchString.Length >= 4 - && char.IsLetter(querySearchString[0]) - && querySearchString[1] == ':' - && querySearchString[2] == '\\' - && char.IsLetter(querySearchString[3])) - return true; - - return false; } public List TopLevelFolderSearchBehaviour( From d839ecab26f74ab1f8ad54b6c74854364e3e64fc Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 May 2020 19:19:54 +1000 Subject: [PATCH 057/142] make Context static so can be called anywhere --- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index e5c9e8e2f..e3f9e9abc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -8,9 +8,9 @@ using System.Windows.Controls; namespace Flow.Launcher.Plugin.Explorer { - class Main : ISettingProvider, IPlugin, ISavable, IContextMenu //, IPluginI18n <=== do later + public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu //, IPluginI18n <=== do later { - private PluginInitContext Context { get; set; } + internal static PluginInitContext Context { get; set; } private Settings _settings; From 6badecb8770dd0416a9c41a5f2e0d442c2cc9994 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 May 2020 20:14:36 +1000 Subject: [PATCH 058/142] Add TopLevelDirecotry search using DirectoryInfo --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 13 +- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 4 +- .../DirectoryInfo/DirectoryInfoSearch.cs | 114 ++++++++++++++++++ .../Search/ResultManager.cs | 51 ++++++++ .../Search/SearchManager.cs | 31 +++-- 5 files changed, 191 insertions(+), 22 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 4ca665cab..283e19fdf 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -2,6 +2,7 @@ using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.Explorer; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; using System; using System.Collections.Generic; @@ -16,7 +17,7 @@ namespace Flow.Launcher.Test.Plugins return new List(); } - private List MethodDirectoryInfoClassSearchReturnsTwoResults(string dummyString) + private List MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString) { return new List { @@ -111,7 +112,8 @@ namespace Flow.Launcher.Test.Plugins MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, MethodIndexExistsReturnsFalse, - "path string not used"); + new Query(), + "string not used"); // Then Assert.IsTrue(results.Count == 2, @@ -130,7 +132,8 @@ namespace Flow.Launcher.Test.Plugins MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, MethodIndexExistsReturnsTrue, - "path string not used"); + new Query(), + "string not used"); // Then Assert.IsTrue(results.Count == 0, @@ -149,9 +152,7 @@ namespace Flow.Launcher.Test.Plugins public void WhenGivenQuerySearchString_ThenShouldIndicateIfItIsLocationString(string querySearchString, bool expectedResult) { // When, Given - var searchManager = new SearchManager(new Settings(), new PluginInitContext()); - - var result = searchManager.IsLocationPathString(querySearchString); + var result = FilesFolders.IsLocationPathString(querySearchString); //Then Assert.IsTrue(result == expectedResult, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index e3f9e9abc..e23fbdb78 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.ViewModels; using Flow.Launcher.Plugin.Explorer.Views; @@ -42,8 +42,6 @@ namespace Flow.Launcher.Plugin.Explorer return results; return new SearchManager(_settings, Context).Search(query); - - return new SearchManager(_settings, Context).Search(query.Search); } public void Save() diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs new file mode 100644 index 000000000..7b55697a9 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -0,0 +1,114 @@ +using Flow.Launcher.Infrastructure; +using Flow.Launcher.Plugin.SharedCommands; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows; + +namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo +{ + public class DirectoryInfoSearch + { + private PluginInitContext _context; + + private Settings _settings; + + public DirectoryInfoSearch(Settings settings, PluginInitContext context) + { + _context = context; + _settings = settings; + } + public List TopLevelDirectorySearch(Query query, string search) + { + var results = new List(); + //var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0; + string incompleteName = ""; + //if (hasSpecial || !Directory.Exists(search + "\\")) + if (!FilesFolders.LocationExists(search + "\\")) + { + // if folder doesn't exist, we want to take the last part and use it afterwards to help the user + // find the right folder. + int index = search.LastIndexOf('\\'); + if (index > 0 && index < (search.Length - 1)) + { + incompleteName = search.Substring(index + 1).ToLower(); + search = search.Substring(0, index + 1); + if (!FilesFolders.LocationExists(search)) + { + return results; + } + } + else + { + return results; + } + } + else + { + // folder exist, add \ at the end of doesn't exist + if (!search.EndsWith("\\")) + { + search += "\\"; + } + } + + results.Add(new ResultManager().CreateOpenCurrentFolderResult(incompleteName, search)); + + var searchOption = SearchOption.TopDirectoryOnly; + incompleteName += "*"; + + //// give the ability to search all folder when starting with > + //if (incompleteName.StartsWith(">")) + //{ + // searchOption = SearchOption.AllDirectories; + + // // match everything before and after search term using supported wildcard '*', ie. *searchterm* + // incompleteName = "*" + incompleteName.Substring(1); + //} + + var folderList = new List(); + var fileList = new List(); + + var folderSubtitleString = Constants.DefaultFolderSubtitleString; + + try + { + // search folder and add results + var directoryInfo = new System.IO.DirectoryInfo(search); + var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption); + + foreach (var fileSystemInfo in fileSystemInfos) + { + if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue; + + if (fileSystemInfo is System.IO.DirectoryInfo) + { + if (searchOption == SearchOption.AllDirectories) + folderSubtitleString = fileSystemInfo.FullName; + + folderList.Add(new ResultManager().CreateFolderResult(fileSystemInfo.Name, folderSubtitleString, fileSystemInfo.FullName, query)); + } + else + { + fileList.Add(new ResultManager().CreateFileResult(fileSystemInfo.FullName, query)); + } + } + } + catch (Exception e) + { + if (e is UnauthorizedAccessException || e is ArgumentException) + { + results.Add(new Result { Title = e.Message, Score = 501 }); + + return results; + } + + throw; + } + + // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. + return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList(); //<===== MOVE ORDERING OUT + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 3ee7848d8..796650e58 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -2,6 +2,8 @@ using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Text; using System.Windows; @@ -42,6 +44,55 @@ namespace Flow.Launcher.Plugin.Explorer.Search ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path } }; } + + public Result CreateOpenCurrentFolderResult(string incompleteName, string search) + { + var firstResult = "Open current directory"; + if (incompleteName.Length > 0) + firstResult = "Open " + search; + + var folderName = search.TrimEnd('\\').Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + + return new Result + { + Title = firstResult, + SubTitle = $"Use > to search files and subfolders within {folderName}, " + + $"* to search for file extensions in {folderName} or both >* to combine the search", + IcoPath = search, + Score = 500, + Action = c => + { + FilesFolders.OpenPath(search); + return true; + } + }; + } + + public Result CreateFileResult(string filePath, Query query) + { + var result = new Result + { + Title = Path.GetFileName(filePath), + SubTitle = filePath, + IcoPath = filePath, + TitleHighlightData = StringMatcher.FuzzySearch(query.Search, Path.GetFileName(filePath)).MatchData, + Action = c => + { + try + { + FilesFolders.OpenPath(filePath); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Could not start " + filePath); + } + + return true; + }, + ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath } + }; + return result; + } } public class SearchResult diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 1f2327e5c..6119f635f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,6 @@ +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; @@ -18,7 +20,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search searcher = new IndexSearcher(_context); } - public List Search(string querySearchString) + internal List Search(Query query) { var querySearch = query.Search; @@ -34,33 +36,36 @@ namespace Flow.Launcher.Plugin.Explorer.Search querySearch = EnvironmentVariables.TranslateEnvironmentVariablePath(querySearch); } + if (FilesFolders.IsLocationPathString(querySearch)) + { return TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, DirectoryInfoClassSearch, WindowsIndexExists, - querySearchString); + query, + querySearch); } - return WindowsIndexFilesAndFoldersSearch(querySearchString); + return WindowsIndexFilesAndFoldersSearch(querySearch); } - private List DirectoryInfoClassSearch(string arg) + private List DirectoryInfoClassSearch(Query query, string querySearch) { - throw new NotImplementedException(); - } + var directoryInfoSearch = new DirectoryInfoSearch(_settings, _context); - /// + return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch); } public List TopLevelFolderSearchBehaviour( Func> windowsIndexSearch, - Func> directoryInfoClassSearch, + Func> directoryInfoClassSearch, Func indexExists, - string path) + Query query, + string querySearchString) { - var results = windowsIndexSearch(path); + var results = windowsIndexSearch(querySearchString); - if (results.Count == 0 && !indexExists(path)) - return directoryInfoClassSearch(path); + if (results.Count == 0 && !indexExists(querySearchString)) + return directoryInfoClassSearch(query, querySearchString); return results; } @@ -73,7 +78,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForAllFilesAndFolders); } - + private List WindowsIndexTopLevelFolderSearch(string path) { var queryConstructor = new QueryConstructor(_settings); From 5150d8a1f63e5253f252099401fe89fd65aa5c4c Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 May 2020 07:37:05 +1000 Subject: [PATCH 059/142] Add DirectryAllFilesFolders method to DirectoryInfo search --- .../DirectoryInfo/DirectoryInfoSearch.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 7b55697a9..9462baad7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -19,13 +19,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo _context = context; _settings = settings; } - public List TopLevelDirectorySearch(Query query, string search) + + internal List DirectoryAllFilesFoldersSearch(Query query, string search) + { + return DirectorySearch(SearchOption.AllDirectories, query, search); + } + + internal List TopLevelDirectorySearch(Query query, string search) + { + return DirectorySearch(SearchOption.TopDirectoryOnly, query, search); + } + + private List DirectorySearch(SearchOption searchOption, Query query, string search) { var results = new List(); //var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0; string incompleteName = ""; //if (hasSpecial || !Directory.Exists(search + "\\")) - if (!FilesFolders.LocationExists(search + "\\")) + if (!Directory.Exists(search + "\\")) { // if folder doesn't exist, we want to take the last part and use it afterwards to help the user // find the right folder. @@ -34,7 +45,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { incompleteName = search.Substring(index + 1).ToLower(); search = search.Substring(0, index + 1); - if (!FilesFolders.LocationExists(search)) + if (!Directory.Exists(search)) { return results; } @@ -55,7 +66,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo results.Add(new ResultManager().CreateOpenCurrentFolderResult(incompleteName, search)); - var searchOption = SearchOption.TopDirectoryOnly; incompleteName += "*"; //// give the ability to search all folder when starting with > @@ -108,7 +118,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo } // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. - return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList(); //<===== MOVE ORDERING OUT + return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList(); } } } From 327ee2d0912e8c619754a6dd5e7779ad0aa18deb Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 May 2020 08:14:21 +1000 Subject: [PATCH 060/142] Add QuickFolderAccess functionality --- .../Search/QuickFolderLinks/FolderLink.cs | 18 +++++++++++++++ .../QuickFolderLinks/QuickFolderAccess.cs | 23 +++++++++++++++++++ .../Search/SearchManager.cs | 8 +++++++ .../Flow.Launcher.Plugin.Explorer/Settings.cs | 11 ++++++--- 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/FolderLink.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/FolderLink.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/FolderLink.cs new file mode 100644 index 000000000..33c6d37dc --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/FolderLink.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Linq; + +namespace Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks +{ + [JsonObject(MemberSerialization.OptIn)] + public class FolderLink + { + [JsonProperty] + public string Path { get; set; } + + public string Nickname => + Path.Split(new[] { System.IO.Path.DirectorySeparatorChar }, StringSplitOptions.None) + .Last() + + " (" + System.IO.Path.GetDirectoryName(Path) + ")"; + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs new file mode 100644 index 000000000..11c735ea2 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs @@ -0,0 +1,23 @@ +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.SharedCommands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +namespace Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks +{ + public class QuickFolderAccess + { + internal List FolderList(Query query, List folderLinks) + { + string search = query.Search.ToLower(); + var userFolderLinks = folderLinks.Where( + x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase)); + var results = userFolderLinks.Select(item => + new ResultManager().CreateFolderResult(item.Nickname, Constants.DefaultFolderSubtitleString, item.Path, query)).ToList(); + return results; + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 6119f635f..3cb2cf842 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,5 @@ using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; using System; @@ -13,6 +14,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search private IndexSearcher searcher; + private QuickFolderAccess quickFolderAccess = new QuickFolderAccess(); + public SearchManager(Settings settings, PluginInitContext context) { _settings = settings; @@ -24,6 +27,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search { var querySearch = query.Search; + var quickFolderLinks = quickFolderAccess.FolderList(query, _settings.FolderLinks); + + if (quickFolderLinks.Count > 0) + return quickFolderLinks; + if (EnvironmentVariables.IsEnvironmentVariableSearch(querySearch)) { return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 2d664c3db..c8eb87c34 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,11 +1,16 @@ -using System; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; +using Newtonsoft.Json; using System.Collections.Generic; -using System.Text; namespace Flow.Launcher.Plugin.Explorer { public class Settings { - public int MaxResult { get; set; } = 100; + [JsonProperty] + public int MaxResult { get; set; } = 100; + + [JsonProperty] + public List FolderLinks { get; set; } = new List(); } } From 08a3215c9df2d6e215dd318e1b77d30e527685da Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 May 2020 18:55:50 +1000 Subject: [PATCH 061/142] Make ResultManager static --- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 6 +++--- .../Search/EnvironmentVariables.cs | 4 ++-- .../Search/QuickFolderLinks/QuickFolderAccess.cs | 2 +- .../Search/ResultManager.cs | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 9462baad7..fefdfb983 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -64,7 +64,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo } } - results.Add(new ResultManager().CreateOpenCurrentFolderResult(incompleteName, search)); + results.Add(ResultManager.CreateOpenCurrentFolderResult(incompleteName, search)); incompleteName += "*"; @@ -97,11 +97,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo if (searchOption == SearchOption.AllDirectories) folderSubtitleString = fileSystemInfo.FullName; - folderList.Add(new ResultManager().CreateFolderResult(fileSystemInfo.Name, folderSubtitleString, fileSystemInfo.FullName, query)); + folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, folderSubtitleString, fileSystemInfo.FullName, query)); } else { - fileList.Add(new ResultManager().CreateFileResult(fileSystemInfo.FullName, query)); + fileList.Add(ResultManager.CreateFileResult(fileSystemInfo.FullName, query)); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index dc48512aa..a62bfb647 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -62,7 +62,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { var expandedPath = environmentVariables[search]; - results.Add(new ResultManager().CreateFolderResult($"%{search}%", expandedPath, expandedPath, query)); + results.Add(ResultManager.CreateFolderResult($"%{search}%", expandedPath, expandedPath, query)); return results; } @@ -81,7 +81,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { if (p.Key.StartsWith(search)) { - results.Add(new ResultManager().CreateFolderResult($"%{p.Key}%", p.Value, p.Value, query)); + results.Add(ResultManager.CreateFolderResult($"%{p.Key}%", p.Value, p.Value, query)); } } return results; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs index 11c735ea2..4bddf8168 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs @@ -16,7 +16,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks var userFolderLinks = folderLinks.Where( x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase)); var results = userFolderLinks.Select(item => - new ResultManager().CreateFolderResult(item.Nickname, Constants.DefaultFolderSubtitleString, item.Path, query)).ToList(); + ResultManager.CreateFolderResult(item.Nickname, Constants.DefaultFolderSubtitleString, item.Path, query)).ToList(); return results; } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 796650e58..9de1bb93c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -9,9 +9,9 @@ using System.Windows; namespace Flow.Launcher.Plugin.Explorer.Search { - public class ResultManager + internal static class ResultManager { - public Result CreateFolderResult(string title, string subtitle, string path, Query query) + internal static Result CreateFolderResult(string title, string subtitle, string path, Query query) { return new Result { @@ -45,7 +45,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search }; } - public Result CreateOpenCurrentFolderResult(string incompleteName, string search) + internal static Result CreateOpenCurrentFolderResult(string incompleteName, string search) { var firstResult = "Open current directory"; if (incompleteName.Length > 0) @@ -68,7 +68,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search }; } - public Result CreateFileResult(string filePath, Query query) + internal static Result CreateFileResult(string filePath, Query query) { var result = new Result { @@ -95,13 +95,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search } } - public class SearchResult + internal class SearchResult { public string FullPath { get; set; } public ResultType Type { get; set; } } - public enum ResultType + internal enum ResultType { Volume, Folder, From b737811bdc51362069ed688ff9a2ca86d23f7591 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 May 2020 18:58:04 +1000 Subject: [PATCH 062/142] Add ContextMenu --- .../ContextMenu.cs | 199 ++++++++++++++++++ Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 6 +- 2 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs new file mode 100644 index 000000000..3ccffd45a --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using System.Windows; +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Plugin.SharedCommands; +using Flow.Launcher.Plugin.Explorer.Search; + +namespace Flow.Launcher.Plugin.Explorer +{ + internal class ContextMenu : IContextMenu + { + public List LoadContextMenus(Result selectedResult) + { + var contextMenus = new List(); + if (selectedResult.ContextData is SearchResult record) + { + if (record.Type == ResultType.File) + { + contextMenus.Add(CreateOpenWithEditorResult(record)); + contextMenus.Add(CreateOpenContainingFolderResult(record)); + } + + var icoPath = (record.Type == ResultType.File) ? Constants.FileImagePath : Constants.FolderImagePath; + var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder"; + contextMenus.Add(new Result + { + Title = "Copy path", + SubTitle = $"Copy the current {fileOrFolder} path to clipboard", + Action = (context) => + { + try + { + Clipboard.SetText(record.FullPath); + return true; + } + catch (Exception e) + { + var message = "Fail to set text in clipboard"; + LogException(message, e); + Main.Context.API.ShowMsg(message); + return false; + } + }, + IcoPath = Constants.CopyImagePath + }); + + contextMenus.Add(new Result + { + Title = $"Copy {fileOrFolder}", + SubTitle = $"Copy the {fileOrFolder} to clipboard", + Action = (context) => + { + try + { + Clipboard.SetFileDropList(new System.Collections.Specialized.StringCollection { record.FullPath }); + return true; + } + catch (Exception e) + { + var message = $"Fail to set {fileOrFolder} in clipboard"; + LogException(message, e); + Main.Context.API.ShowMsg(message); + return false; + } + + }, + IcoPath = icoPath + }); + + if (record.Type == ResultType.File || record.Type == ResultType.Folder) + contextMenus.Add(new Result + { + Title = $"Delete {fileOrFolder}", + SubTitle = $"Delete the selected {fileOrFolder}", + Action = (context) => + { + try + { + if (record.Type == ResultType.File) + File.Delete(record.FullPath); + else + Directory.Delete(record.FullPath, true); + } + catch (Exception e) + { + var message = $"Fail to delete {fileOrFolder} at {record.FullPath}"; + LogException(message, e); + Main.Context.API.ShowMsg(message); + return false; + } + + return true; + }, + IcoPath = Constants.DeleteFileFolderImagePath + }); + + if (record.Type == ResultType.File && CanRunAsDifferentUser(record.FullPath)) + contextMenus.Add(new Result + { + Title = "Run as different user", + Action = (context) => + { + try + { + Task.Run(() => ShellCommand.RunAsDifferentUser(record.FullPath.SetProcessStartInfo())); + } + catch (FileNotFoundException e) + { + var name = "Plugin: Folder"; + var message = $"File not found: {e.Message}"; + Main.Context.API.ShowMsg(name, message); + } + + return true; + }, + IcoPath = "Images/user.png" + }); + } + + return contextMenus; + } + + private Result CreateOpenContainingFolderResult(SearchResult record) + { + return new Result + { + Title = "Open containing folder", + Action = _ => + { + try + { + Process.Start("explorer.exe", $" /select,\"{record.FullPath}\""); + } + catch (Exception e) + { + var message = $"Fail to open file at {record.FullPath}"; + LogException(message, e); + Main.Context.API.ShowMsg(message); + return false; + } + + return true; + }, + IcoPath = Constants.FolderImagePath + }; + } + + + private Result CreateOpenWithEditorResult(SearchResult record) + { + string editorPath = "notepad.exe"; // TODO add the ability to create a custom editor + + var name = "Open With Editor: " + Path.GetFileNameWithoutExtension(editorPath); + return new Result + { + Title = name, + Action = _ => + { + try + { + Process.Start(editorPath, record.FullPath); + return true; + } + catch (Exception e) + { + var message = $"Fail to editor for file at {record.FullPath}"; + LogException(message, e); + Main.Context.API.ShowMsg(message); + return false; + } + }, + IcoPath = editorPath + }; + } + + public void LogException(string message, Exception e) + { + Log.Exception($"|Flow.Launcher.Plugin.Folder.ContextMenu|{message}", e); + } + + private bool CanRunAsDifferentUser(string path) + { + switch (Path.GetExtension(path)) + { + case ".exe": + case ".bat": + case ".msi": + return true; + + default: + return false; + + } + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index e23fbdb78..8bd6812d2 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -16,6 +16,8 @@ namespace Flow.Launcher.Plugin.Explorer private SettingsViewModel _viewModel; + private IContextMenu _contextMenu; + public Control CreateSettingPanel() { return new ExplorerSettings(); @@ -24,14 +26,14 @@ namespace Flow.Launcher.Plugin.Explorer public void Init(PluginInitContext context) { Context = context; - _viewModel = new SettingsViewModel(); _settings = _viewModel.Settings; + _contextMenu = new ContextMenu(); } public List LoadContextMenus(Result selectedResult) { - return new List(); + return _contextMenu.LoadContextMenus(selectedResult); } public List Query(Query query) From f8a4b924c89b898f826fb86c4d12eaf9404238ec Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 May 2020 18:58:28 +1000 Subject: [PATCH 063/142] IndexSearch use ResultManager --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 6 ++-- .../Search/SearchManager.cs | 16 +++++---- .../Search/WindowsIndex/IndexSearcher.cs | 34 ++++++------------- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 283e19fdf..fc247d858 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Test.Plugins [TestFixture] public class ExplorerTest { - private List MethodWindowsIndexSearchReturnsZeroResults(string dummyString) + private List MethodWindowsIndexSearchReturnsZeroResults(Query dummyQuery, string dummyString) { return new List(); } @@ -141,8 +141,6 @@ namespace Flow.Launcher.Test.Plugins $"Actual number of results is {results.Count} {Environment.NewLine}"); } - public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString() { } - [TestCase(@"c:\\", false)] [TestCase(@"i:\", true)] [TestCase(@"\c:\", false)] @@ -160,5 +158,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual check result is {result} {Environment.NewLine}"); } + + public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString() { } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 3cb2cf842..c06d1db45 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -53,7 +53,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search querySearch); } - return WindowsIndexFilesAndFoldersSearch(querySearch); + return WindowsIndexFilesAndFoldersSearch(query, querySearch); } private List DirectoryInfoClassSearch(Query query, string querySearch) @@ -64,13 +64,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search } public List TopLevelFolderSearchBehaviour( - Func> windowsIndexSearch, + Func> windowsIndexSearch, Func> directoryInfoClassSearch, Func indexExists, Query query, string querySearchString) { - var results = windowsIndexSearch(querySearchString); + var results = windowsIndexSearch(query, querySearchString); if (results.Count == 0 && !indexExists(querySearchString)) return directoryInfoClassSearch(query, querySearchString); @@ -78,22 +78,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search return results; } - private List WindowsIndexFilesAndFoldersSearch(string querySearchString) + private List WindowsIndexFilesAndFoldersSearch(Query query, string querySearchString) { var queryConstructor = new QueryConstructor(_settings); return searcher.WindowsIndexSearch(querySearchString, queryConstructor.CreateQueryHelper().ConnectionString, - queryConstructor.QueryForAllFilesAndFolders); + queryConstructor.QueryForAllFilesAndFolders, + query); } - private List WindowsIndexTopLevelFolderSearch(string path) + private List WindowsIndexTopLevelFolderSearch(Query query, string path) { var queryConstructor = new QueryConstructor(_settings); return searcher.WindowsIndexSearch(path, queryConstructor.CreateQueryHelper().ConnectionString, - queryConstructor.QueryForTopLevelDirectorySearch); + queryConstructor.QueryForTopLevelDirectorySearch, + query); } private bool WindowsIndexExists(string path) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs index 6081bf119..b7f172892 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs @@ -30,7 +30,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex _context = context; } - internal List ExecuteWindowsIndexSearch(string searchString, string connectionString) + internal List ExecuteWindowsIndexSearch(string searchString, string connectionString, Query query) { var results = new List(); @@ -54,7 +54,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex results.Add(CreateResult( dataReaderResults.GetString(0), dataReaderResults.GetString(1), - dataReaderResults.GetString(2))); + dataReaderResults.GetString(2), + query)); } } } @@ -74,30 +75,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return results; } - private Result CreateResult(string filename, string path, string fileType) + private Result CreateResult(string filename, string path, string fileType, Query query) { - return new Result + if (fileType == "Directory") + return ResultManager.CreateFolderResult(filename, path, path, query); + else { - Title = filename, - SubTitle = path, - IcoPath = fileType == "Directory" ? Constants.FolderImagePath : Constants.FileImagePath, - Action = c => - { - try - { - FilesFolders.OpenPath(path); - } - catch (Win32Exception) - { - _context.API.ShowMsg("Explorer plugin: ", "Unable to open the selected file", string.Empty); //<========= - } - - return true; - } - }; + return ResultManager.CreateFileResult(path, query); + } } - internal List WindowsIndexSearch(string searchString, string connectionString, Func constructQuery) + internal List WindowsIndexSearch(string searchString, string connectionString, Func constructQuery, Query query) { var regexMatch = Regex.Match(searchString, ReservedStringPattern); @@ -107,7 +95,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex lock (_lock) { var constructedQuery = constructQuery(searchString); - return ExecuteWindowsIndexSearch(constructedQuery, connectionString); + return ExecuteWindowsIndexSearch(constructedQuery, connectionString, query); } } From b1a0ddfe2f7fd33e5ff70f64cc25f9958103f1f8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 May 2020 19:01:31 +1000 Subject: [PATCH 064/142] Remove unused context from IndexSearch --- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- .../Search/WindowsIndex/IndexSearcher.cs | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index c06d1db45..b1d00acbb 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -20,7 +20,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { _settings = settings; _context = context; - searcher = new IndexSearcher(_context); + searcher = new IndexSearcher(); } internal List Search(Query query) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs index b7f172892..79c5df68c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs @@ -20,16 +20,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex public OleDbDataReader dataReaderResults; - private PluginInitContext _context; - // Reserved keywords in oleDB private string ReservedStringPattern = @"^[\/\\\$\%]+$"; - public IndexSearcher(PluginInitContext context) - { - _context = context; - } - internal List ExecuteWindowsIndexSearch(string searchString, string connectionString, Query query) { var results = new List(); From b03ee666462313bfe02b7d7e7c754e191e03877a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 May 2020 19:06:09 +1000 Subject: [PATCH 065/142] Rename IndexSearcher --- .../Search/SearchManager.cs | 10 +++++----- .../WindowsIndex/{IndexSearcher.cs => IndexSearch.cs} | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) rename Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/{IndexSearcher.cs => IndexSearch.cs} (99%) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index b1d00acbb..4bae801b3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search private Settings _settings; private PluginInitContext _context; - private IndexSearcher searcher; + private IndexSearch _indexSearch; private QuickFolderAccess quickFolderAccess = new QuickFolderAccess(); @@ -20,7 +20,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { _settings = settings; _context = context; - searcher = new IndexSearcher(); + _indexSearch = new IndexSearch(); } internal List Search(Query query) @@ -82,7 +82,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { var queryConstructor = new QueryConstructor(_settings); - return searcher.WindowsIndexSearch(querySearchString, + return _indexSearch.WindowsIndexSearch(querySearchString, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForAllFilesAndFolders, query); @@ -92,7 +92,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { var queryConstructor = new QueryConstructor(_settings); - return searcher.WindowsIndexSearch(path, + return _indexSearch.WindowsIndexSearch(path, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForTopLevelDirectorySearch, query); @@ -100,7 +100,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search private bool WindowsIndexExists(string path) { - return searcher.PathIsIndexed(path); + return _indexSearch.PathIsIndexed(path); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs similarity index 99% rename from Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs rename to Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 79c5df68c..2648b82be 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearcher.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -10,7 +10,7 @@ using System.Text.RegularExpressions; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { - internal class IndexSearcher + internal class IndexSearch { private readonly object _lock = new object(); From 74b25c0bf1ec1809a50fe009625e31c9a99b2434 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 09:03:27 +1000 Subject: [PATCH 066/142] Add first result as opening current directory --- .../Search/ResultManager.cs | 31 ++++++++++++---- .../Search/SearchManager.cs | 36 ++++++++++++++++--- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 9de1bb93c..ef392f920 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -45,24 +45,41 @@ namespace Flow.Launcher.Plugin.Explorer.Search }; } - internal static Result CreateOpenCurrentFolderResult(string incompleteName, string search) + internal static Result CreateOpenCurrentFolderResult(string path, bool isPreviousDirectoryLevel) { - var firstResult = "Open current directory"; - if (incompleteName.Length > 0) - firstResult = "Open " + search; + var folderName = path; - var folderName = search.TrimEnd('\\').Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + if (folderName.EndsWith(":\\")) + { + var driveLetter = folderName.Substring(0, 1).ToUpper(); + folderName = driveLetter + " drive"; + } + else + { + folderName = folderName.TrimEnd('\\').Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + } + + var firstResult = ""; + + if (isPreviousDirectoryLevel) + { + firstResult = "Open " + folderName; + } + else + { + firstResult = "Open current directory"; + } return new Result { Title = firstResult, SubTitle = $"Use > to search files and subfolders within {folderName}, " + $"* to search for file extensions in {folderName} or both >* to combine the search", - IcoPath = search, + IcoPath = path, Score = 500, Action = c => { - FilesFolders.OpenPath(search); + FilesFolders.OpenPath(path); return true; } }; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 4bae801b3..155b89831 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; @@ -44,9 +44,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search querySearch = EnvironmentVariables.TranslateEnvironmentVariablePath(querySearch); } - if (FilesFolders.IsLocationPathString(querySearch)) - { - return TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, + var results = new List(); + + var currentFolderResult = CreateOpenCurrentFolderResult(FilesFolders.LocationExists, querySearch); + + if (currentFolderResult == null) + return new List(); + + results.Add(currentFolderResult); DirectoryInfoClassSearch, WindowsIndexExists, query, @@ -102,5 +107,28 @@ namespace Flow.Launcher.Plugin.Explorer.Search { return _indexSearch.PathIsIndexed(path); } + + private Result CreateOpenCurrentFolderResult(Func locationExists, string querySearchString) + { + if (locationExists(querySearchString)) + return ResultManager.CreateOpenCurrentFolderResult(querySearchString, false); + + var partialPath = ""; + int index = querySearchString.LastIndexOf('\\'); + if (index > 0 && index < (querySearchString.Length - 1)) + { + partialPath = querySearchString.Substring(0, index + 1); + if (!locationExists(partialPath)) + { + return null; + } + } + else + { + return null; + } + + return ResultManager.CreateOpenCurrentFolderResult(partialPath, true); + } } } From 0ba9a86e465821f282bb181097ec3689301e0d84 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 13:46:59 +1000 Subject: [PATCH 067/142] Clean up method --- .../DirectoryInfo/DirectoryInfoSearch.cs | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index fefdfb983..4823c987b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -36,35 +36,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo //var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0; string incompleteName = ""; //if (hasSpecial || !Directory.Exists(search + "\\")) - if (!Directory.Exists(search + "\\")) + + // folder exist, add \ at the end of doesn't exist + if (!search.EndsWith("\\")) { - // if folder doesn't exist, we want to take the last part and use it afterwards to help the user - // find the right folder. - int index = search.LastIndexOf('\\'); - if (index > 0 && index < (search.Length - 1)) - { - incompleteName = search.Substring(index + 1).ToLower(); - search = search.Substring(0, index + 1); - if (!Directory.Exists(search)) - { - return results; - } - } - else - { - return results; - } + search += "\\"; } - else - { - // folder exist, add \ at the end of doesn't exist - if (!search.EndsWith("\\")) - { - search += "\\"; - } - } - - results.Add(ResultManager.CreateOpenCurrentFolderResult(incompleteName, search)); incompleteName += "*"; @@ -118,7 +95,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo } // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. - return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList(); + return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList(); //<============= MOVE OUT } } } From 5cf85b46dedcba1932c282a72e6206e4a15e06df Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 14:11:20 +1000 Subject: [PATCH 068/142] per comment Add refer to logs message + warning icon --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index afbc18fdb..925083536 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Loader; using System.Threading.Tasks; -using System.Windows; +using System.Windows.Forms; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; @@ -117,7 +117,10 @@ namespace Flow.Launcher.Core.Plugin Task.Run(() => { - MessageBox.Show($"{errorMessage}{Environment.NewLine}{errorPluginString}"); + MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine} " + + $"Please refer to the logs for more information","", + MessageBoxButtons.OK, MessageBoxIcon.Warning); }); } From 7f23ac49092635b810b34c6b31b9aa3759535f1c Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 19:10:40 +1000 Subject: [PATCH 069/142] remove unintended space before text --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 925083536..513d85c96 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -118,7 +118,7 @@ namespace Flow.Launcher.Core.Plugin Task.Run(() => { MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + - $"{errorPluginString}{Environment.NewLine}{Environment.NewLine} " + + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + $"Please refer to the logs for more information","", MessageBoxButtons.OK, MessageBoxIcon.Warning); }); From 900502e079ea71ecf4eb9d00fd5b2e1732e99608 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 19:34:45 +1000 Subject: [PATCH 070/142] Update test assert string text --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index fc247d858..90bfd88f3 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -64,7 +64,7 @@ namespace Flow.Launcher.Test.Plugins // Then Assert.IsTrue(queryString == expectedString, - $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + + $"Expected string: {expectedString}{Environment.NewLine} " + $"Actual string was: {queryString}{Environment.NewLine}"); } From 2feba97f561b34cfa6f4ff57e8a53ea9c09c9ee8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 19:35:30 +1000 Subject: [PATCH 071/142] Add tests for index searching partial folder or file name --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 90bfd88f3..8c2612477 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -68,6 +68,43 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {queryString}{Environment.NewLine}"); } + [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\", \"System.ItemType\" " + + "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" + + " AND directory='file:C:\\Dropbox'")] + public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString( + string userSearchString, string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + //When + var queryString = queryConstructor.QueryForTopLevelDirectorySearch(userSearchString); + + // Then + Assert.IsTrue(queryString == expectedString, + $"Expected string: {expectedString}{Environment.NewLine} " + + $"Actual string was: {queryString}{Environment.NewLine}"); + } + + [TestCase("C:\\Dropbox\\App", "WHERE (System.FileName LIKE 'App%' " + + "OR CONTAINS(System.FileName,'\"App*\"',1033))" + + " AND directory='file:C:\\Dropbox'")] + public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryWhereRestrictionsShouldUseDirectoryString( + string userSearchString, string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + //When + var queryString = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(userSearchString); + + // Then + Assert.IsTrue(queryString == expectedString, + $"Expected string: {expectedString}{Environment.NewLine} " + + $"Actual string was: {queryString}{Environment.NewLine}"); + } + [TestCase("scope='file:'")] public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString) { From 436cfe2e2bf9640417ba2d8e732a0686abea5709 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 19:49:18 +1000 Subject: [PATCH 072/142] Add tests for GetPreviousDirectory method --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 8c2612477..12121ec85 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -37,6 +37,10 @@ namespace Flow.Launcher.Test.Plugins private bool MethodIndexExistsReturnsFalse(string dummyString) => false; + private bool LocationExistsReturnsTrue(string dummyString) => true; + + private bool LocationNotExistReturnsFalse(string dummyString) => false; + [TestCase("C:\\Dropbox", "directory='file:C:\\Dropbox'")] public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString) { @@ -195,6 +199,19 @@ namespace Flow.Launcher.Test.Plugins $"Actual check result is {result} {Environment.NewLine}"); } + + [TestCase(@"C:\Dropbox\Drop", @"C:\Dropbox")] + [TestCase(@"C:\Dropbox\Drop\App", @"C:\Dropbox\Drop")] + public void GivenAPartialPath_WhenPreviousLevelDirectoryExists_ThenShouldReturnThePreviousDirectoryPathString() + { + + } + + [TestCase(@"C:\Dropbox\Drop", "")] + public void GivenAPartialPath_WhenPreviousLevelDirectoryNotExists_ThenShouldReturnEmptyString() + { + + } public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString() { } } From 2d4e2f566272936ab829fee3014830d9dfd5c651 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 20:18:18 +1000 Subject: [PATCH 073/142] Add methods to test for previous directory location --- .../SharedCommands/FilesFolders.cs | 25 ++++++++++++++ Flow.Launcher.Test/Plugins/ExplorerTest.cs | 33 +++++++++++++------ .../Search/SearchManager.cs | 20 +++-------- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 440ac6756..bac3c212b 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -160,5 +160,30 @@ namespace Flow.Launcher.Plugin.SharedCommands return false; } + + /// + /// Gets the previous level directory from a path string. + /// Checks that previous level directory exists and returns it + /// as a path string, or empty string if doesnt exit + /// + public static string GetPreviousExistingDirectory(Func locationExists, string path) + { + var previousDirectoryPath = ""; + int index = path.LastIndexOf('\\'); + if (index > 0 && index < (path.Length - 1)) + { + previousDirectoryPath = path.Substring(0, index + 1); + if (!locationExists(path)) + { + return ""; + } + } + else + { + return ""; + } + + return previousDirectoryPath; + } } } diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 12121ec85..14469242e 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -37,9 +37,9 @@ namespace Flow.Launcher.Test.Plugins private bool MethodIndexExistsReturnsFalse(string dummyString) => false; - private bool LocationExistsReturnsTrue(string dummyString) => true; + private bool PreviousLocationExistsReturnsTrue(string dummyString) => true; - private bool LocationNotExistReturnsFalse(string dummyString) => false; + private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false; [TestCase("C:\\Dropbox", "directory='file:C:\\Dropbox'")] public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString) @@ -200,17 +200,30 @@ namespace Flow.Launcher.Test.Plugins } - [TestCase(@"C:\Dropbox\Drop", @"C:\Dropbox")] - [TestCase(@"C:\Dropbox\Drop\App", @"C:\Dropbox\Drop")] - public void GivenAPartialPath_WhenPreviousLevelDirectoryExists_ThenShouldReturnThePreviousDirectoryPathString() + [TestCase(@"C:\Dropbox\Drop", true, @"C:\Dropbox\")] + [TestCase(@"C:\Dropbox\Drop\App", true, @"C:\Dropbox\Drop\")] + [TestCase(@"C:\Dropbox\Drop", false, "")] + public void GivenAPartialPath_WhenPreviousLevelDirectoryExists_ThenShouldReturnThePreviousDirectoryPathString( + string path, bool previousDirectoryExists, string expectedString) { + // When + Func previousLocationExists = null; + if (previousDirectoryExists) + { + previousLocationExists = PreviousLocationExistsReturnsTrue; + } + else + { + previousLocationExists = PreviousLocationNotExistReturnsFalse; + } - } - - [TestCase(@"C:\Dropbox\Drop", "")] - public void GivenAPartialPath_WhenPreviousLevelDirectoryNotExists_ThenShouldReturnEmptyString() - { + // Given + var previousDirectoryPath = FilesFolders.GetPreviousExistingDirectory(previousLocationExists, path); + //Then + Assert.IsTrue(previousDirectoryPath == expectedString, + $"Expected path string: {expectedString} {Environment.NewLine} " + + $"Actual path string is {previousDirectoryPath} {Environment.NewLine}"); } public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString() { } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 155b89831..19fbdc9e6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -113,22 +113,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (locationExists(querySearchString)) return ResultManager.CreateOpenCurrentFolderResult(querySearchString, false); - var partialPath = ""; - int index = querySearchString.LastIndexOf('\\'); - if (index > 0 && index < (querySearchString.Length - 1)) - { - partialPath = querySearchString.Substring(0, index + 1); - if (!locationExists(partialPath)) - { - return null; - } - } - else - { - return null; - } + var previousDirectoryPath = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, querySearchString); - return ResultManager.CreateOpenCurrentFolderResult(partialPath, true); + if (string.IsNullOrEmpty(previousDirectoryPath)) + return null; + + return ResultManager.CreateOpenCurrentFolderResult(previousDirectoryPath, true); } } } From 355a787b5e125efd21344c1e880e1f71b5e04ddf Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 21:47:04 +1000 Subject: [PATCH 074/142] Fix test expected result --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 14469242e..6007970ba 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -91,7 +91,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {queryString}{Environment.NewLine}"); } - [TestCase("C:\\Dropbox\\App", "WHERE (System.FileName LIKE 'App%' " + + [TestCase("C:\\Dropbox\\App", "(System.FileName LIKE 'App%' " + "OR CONTAINS(System.FileName,'\"App*\"',1033))" + " AND directory='file:C:\\Dropbox'")] public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryWhereRestrictionsShouldUseDirectoryString( From 3caafa325f8e0917372f9a3e0521238fc64bb94d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 21:47:47 +1000 Subject: [PATCH 075/142] Update getting previous directory method --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index bac3c212b..a81ff8008 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -164,16 +164,16 @@ namespace Flow.Launcher.Plugin.SharedCommands /// /// Gets the previous level directory from a path string. /// Checks that previous level directory exists and returns it - /// as a path string, or empty string if doesnt exit + /// as a path string, or empty string if doesn't exit /// public static string GetPreviousExistingDirectory(Func locationExists, string path) { var previousDirectoryPath = ""; - int index = path.LastIndexOf('\\'); + var index = path.LastIndexOf('\\'); if (index > 0 && index < (path.Length - 1)) { previousDirectoryPath = path.Substring(0, index + 1); - if (!locationExists(path)) + if (!locationExists(previousDirectoryPath)) { return ""; } From 353fa026af9e967083102f08e12e69bfb50f6d46 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 21:49:59 +1000 Subject: [PATCH 076/142] rename variables and update comment --- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 3 +-- .../Search/WindowsIndex/IndexSearch.cs | 4 ++-- .../Search/WindowsIndex/QueryConstructor.cs | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 19fbdc9e6..703667714 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -37,8 +37,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query); } - // Query is a location path with a full environment variable- starts with a % - // and contains another % somewhere before the end of the path + // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ if (querySearch.Substring(1).Contains("%")) { querySearch = EnvironmentVariables.TranslateEnvironmentVariablePath(querySearch); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 2648b82be..4f0f8439a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -23,7 +23,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex // Reserved keywords in oleDB private string ReservedStringPattern = @"^[\/\\\$\%]+$"; - internal List ExecuteWindowsIndexSearch(string searchString, string connectionString, Query query) + internal List ExecuteWindowsIndexSearch(string indexQueryString, string connectionString, Query query) { var results = new List(); @@ -33,7 +33,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { conn.Open(); - using (command = new OleDbCommand(searchString, conn)) + using (command = new OleDbCommand(indexQueryString, conn)) { // Results return as an OleDbDataReader. using (dataReaderResults = command.ExecuteReader()) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index d74a11b48..2c2f197c0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -1,4 +1,4 @@ -using Microsoft.Search.Interop; +using Microsoft.Search.Interop; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { @@ -57,11 +57,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex /// /// Search will be performed on all folders and files on the first level of a specified directory. /// - public string QueryForTopLevelDirectorySearch(string folderPath) + public string QueryForTopLevelDirectorySearch(string path) { string query = "SELECT TOP " + _settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; - query += QueryWhereRestrictionsForTopLevelDirectorySearch(folderPath); + query += QueryWhereRestrictionsForTopLevelDirectorySearch(path); return query; } From ea9992475e031d7572c6448bce022775440552dd Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 21:51:05 +1000 Subject: [PATCH 077/142] move method up for readability --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 703667714..2b5744b19 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -32,6 +32,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (quickFolderLinks.Count > 0) return quickFolderLinks; + if (!FilesFolders.IsLocationPathString(querySearch)) + return WindowsIndexFilesAndFoldersSearch(query, querySearch); + if (EnvironmentVariables.IsEnvironmentVariableSearch(querySearch)) { return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query); From 5b2f06bf106a6f34983a7d4b42d874f615f96b05 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 May 2020 21:55:07 +1000 Subject: [PATCH 078/142] add logic for index search to search with partial item name --- .../Search/SearchManager.cs | 15 ++++++++------- .../Search/WindowsIndex/QueryConstructor.cs | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 2b5744b19..1bca58366 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; @@ -54,13 +54,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search return new List(); results.Add(currentFolderResult); - DirectoryInfoClassSearch, - WindowsIndexExists, - query, - querySearch); - } - return WindowsIndexFilesAndFoldersSearch(query, querySearch); + results.AddRange(TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, + DirectoryInfoClassSearch, + WindowsIndexExists, + query, + querySearch)); + + return results; } private List DirectoryInfoClassSearch(Query query, string querySearch) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index 2c2f197c0..cbf896d26 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -51,7 +51,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex /// public string QueryWhereRestrictionsForTopLevelDirectorySearch(string path) { - return $"directory='file:{path}'"; + var directoryWhereRestriction = $"directory='file:"; + + if (path.EndsWith("\\")) + return directoryWhereRestriction + $"{path}'"; + + var indexOfSeparator = path.LastIndexOf('\\'); + + var itemName = path.Substring(indexOfSeparator + 1); + + var previousLevelDirectory = path.Substring(0, indexOfSeparator); + + return $"(System.FileName LIKE '{itemName}%' " + + $"OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND " + + directoryWhereRestriction + $"{previousLevelDirectory}'"; } /// From e22abba18ca8cefd8c31ff5ec86c118fd17d7e51 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 27 May 2020 07:30:27 +1000 Subject: [PATCH 079/142] DirectoryInfoSearch- remove auto open folder via appending '\' to path --- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 4823c987b..11d820913 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure; +using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; @@ -36,15 +36,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo //var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0; string incompleteName = ""; //if (hasSpecial || !Directory.Exists(search + "\\")) - - // folder exist, add \ at the end of doesn't exist - if (!search.EndsWith("\\")) - { - search += "\\"; - } - - incompleteName += "*"; - //// give the ability to search all folder when starting with > //if (incompleteName.StartsWith(">")) //{ From c93f4a09ea56474766dba2d2678bd1ef1292562f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 27 May 2020 07:30:52 +1000 Subject: [PATCH 080/142] DirectoryInfoPath- fix partial item search --- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 11d820913..5511ee8c1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure; +using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; @@ -45,6 +45,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo // incompleteName = "*" + incompleteName.Substring(1); //} + if (!search.EndsWith("\\")) + { + // not full path, get previous level directory string + var indexOfSeparator = search.LastIndexOf('\\'); + + incompleteName = search.Substring(indexOfSeparator + 1).ToLower(); + + search = search.Substring(0, indexOfSeparator + 1); + } + + incompleteName += "*"; + var folderList = new List(); var fileList = new List(); From af00ec58792c46b85762e24b14f7b6e6909f493f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 27 May 2020 07:41:08 +1000 Subject: [PATCH 081/142] remove obsolete variable folderSubtitleString in DirectoryInfoSeach --- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 5511ee8c1..31697aa95 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -60,11 +60,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo var folderList = new List(); var fileList = new List(); - var folderSubtitleString = Constants.DefaultFolderSubtitleString; - try { - // search folder and add results var directoryInfo = new System.IO.DirectoryInfo(search); var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption); @@ -74,10 +71,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo if (fileSystemInfo is System.IO.DirectoryInfo) { - if (searchOption == SearchOption.AllDirectories) - folderSubtitleString = fileSystemInfo.FullName; - - folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, folderSubtitleString, fileSystemInfo.FullName, query)); + folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, fileSystemInfo.FullName, query)); } else { From 42845dadcfa1973b0c689cf47b6e5e0076b75539 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Wed, 27 May 2020 01:37:07 +0300 Subject: [PATCH 082/142] plugin: switch to embedded PDBs --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 4 +--- appveyor.yml | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 3cbe35d6d..69e4c3f22 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -19,8 +19,6 @@ 1.0.0 1.0.0 Flow.Launcher.Plugin - true - snupkg Flow-Launcher MIT https://github.com/Flow-Launcher/Flow.Launcher @@ -43,7 +41,7 @@ - portable + embedded true ..\Output\Release\ TRACE diff --git a/appveyor.yml b/appveyor.yml index 02027f83e..e476bd3b1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,6 +29,4 @@ artifacts: - path: 'Output\Packages\Flow-Launcher-*.zip' name: Zip - path: 'Output\Release\Flow.Launcher.Plugin.*.nupkg' - name: Plugin nupkg -- path: 'Output\Release\Flow.Launcher.Plugin.*.snupkg' - name: Plugin snupkg \ No newline at end of file + name: Plugin nupkg \ No newline at end of file From f8481a843551c9449fb0011cbe7a64005a60c9fa Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Wed, 27 May 2020 01:38:27 +0300 Subject: [PATCH 083/142] plugin: enable deterministic builds in CI --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 69e4c3f22..b3a5b559a 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -28,6 +28,10 @@ true + + true + + true full From 5d6436864d5e01374d7b9a1c9a5e30c842ffabbd Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Wed, 27 May 2020 01:42:27 +0300 Subject: [PATCH 084/142] plugin: remove the SolutionAssemblyInfo file CI patches the version of this file, causing the Plugin project to have the wrong Assembly/File versions. --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index b3a5b559a..6a4cf7d26 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -62,10 +62,6 @@ - - - - From 63e9c5e409a617d290e6068d6308ecf2a9f48783 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Wed, 27 May 2020 01:47:08 +0300 Subject: [PATCH 085/142] plugin: do not pack FodyWeavers.xml --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 6a4cf7d26..6d1bb65de 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -56,14 +56,7 @@ - - - - - - - - + From 07a4c827608e61fa3891ae8634ec0a7cb824fcc8 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Wed, 27 May 2020 01:50:15 +0300 Subject: [PATCH 086/142] plugin: bump version to 1.0.0-beta3 --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 6d1bb65de..ff4700e94 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -15,7 +15,7 @@ 1.0.0 - 1.0.0-beta2 + 1.0.0-beta3 1.0.0 1.0.0 Flow.Launcher.Plugin From 2ea6642248e61d42c06e6b9893ecb01c5dab1305 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 27 May 2020 19:36:49 +1000 Subject: [PATCH 087/142] show if file or folder is indexed in context menu --- .../ContextMenu.cs | 4 ++++ .../Flow.Launcher.Plugin.Explorer.csproj | 4 ++++ .../Images/index.png | Bin 0 -> 19078 bytes .../Search/Constants.cs | 1 + .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 4 ++-- .../Search/ResultManager.cs | 12 ++++++++---- .../Search/WindowsIndex/IndexSearch.cs | 4 ++-- 7 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/index.png diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 3ccffd45a..544b74abe 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -23,6 +23,10 @@ namespace Flow.Launcher.Plugin.Explorer contextMenus.Add(CreateOpenContainingFolderResult(record)); } + if (record.ShowIndexState) + contextMenus.Add(new Result { Title = "Indexed: " + (record.WindowsIndexed ? "Yes" : "No"), + Score = 501, IcoPath = Constants.IndexImagePath }); + var icoPath = (record.Type == ResultType.File) ? Constants.FileImagePath : Constants.FolderImagePath; var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder"; contextMenus.Add(new Result diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 0261da36b..3f3206567 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -27,6 +27,10 @@ PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Images/index.png b/Plugins/Flow.Launcher.Plugin.Explorer/Images/index.png new file mode 100644 index 0000000000000000000000000000000000000000..c63fc8069f08e9e118ab623a0e243aab1d4cbb6a GIT binary patch literal 19078 zcmd732UJu`(jv`_L zLN>? z0|7oMK7^3Kp@aL6B1D2m65`>4|ESW+7vVq2bNZI&%zT{B1v;E{LVHj9I6BE4@^Wx- zI_l(b`n+$YlQu$!l@9ISYaY}#(iwKsX6C%?MC#-3OYQ3;wh$tfEB+P?_)3&fOTO^t zt(W?ZQ90~Z-7acS|E>+Ar36K{j6-);Zt!m--{#ZuQpmAc(dOoP`+-!fbfG#|<{3rf zwWlh#23$W=bwTyhD!N#(o{DQoN}-Ea1m{Rs@m$B$pM|QVcR$|be)DS$q1Mi6mvkTE z1)Bdi|7af`q=8_n%ERV5o4?pvEtCkBj9^7W$5edL!?WZ`P65obrwRU!@GXwNlS@~vO<&5J)XqgU@`80Z{A!q85i^ois_W5Ogh zG3u;pUZU;@gThADDuwloI7OA0438bI;bC(U2jlx=uvhNWMr&6~3~+R0iL$YWt7G*y zz0IzE0v?VJaIld@yECJ{{j|Z>eILB0)$lW)=9|vOG5vg@MCaYG9oMN!btH$=z#UUk z$m%~g@T)qJ{F0xI&|4h?a%V@R%wvv58*qBh{QVD288MiB*L>{L14A(b>lnp{2|R!x z{?JtS7hB!L>r9K7tpql_n^}=1fuxoe|fpVhy7!lEqvhjLXQ_}@pnrlL% zIq2t%0K;_2LdjO+v4sp7N8*nIQ5-~kX==P!eJiQ*o7a)hpoTRxsAK2sJo{T7_>3YY zP7!EGpav_;hy>l=!DhIcntc6N_V&f_`6Mh%8|&_^ng1&58w5I)I*uuj*z$H|ExDS6 zhn@PJS~xRb$n?FEsm3J=2j-8-1Er&Y8zH_{I@VD-<{ zpPdiuPE}AOJwMtvqi^yTdrWY9Qu*NG7$5Vmir2$8Tv;#L;+^xnomAS7YCIvXKl$#j ze^C9dXBPbVRZ>;$s{Tkevw?T!L}yx8PJeIH?PgZi%Q5TaCpg8l-_@PI`4IikC5Uj zU1wPxgPnw6x2#nNo%?>{wg%?gjJa21LQZ*C(2w8h=aJ(H{(+AqN^Lv{XYlsz&J#C$ z|GXWhW(WQf>(1tJcQj-iyvnj%*)%da#-`@a&O4vo&Q6!`KZ*$TX*zi0yvviBu9%;D zDUu4Bv!}<~zjukz#XZ3?4Bi;sxZu}uSHa|NGt&{pr}3NcTB?*1-dE!TMiR=8A40No+_-H)+D`E3wc){L~avhn*^o9vL>t&H4p9a8Ns*%NgwNC4_&vuD}!4s z(Z1!d`3I+3{iD9!H~1isCObgj|Fw>7hUgZr%RQ7rB*#9-cDZ*`H|f#pNBV1YnXYDt z29Wf~nC9g3X@_=~ojy?CQ)Mc1fNeeAZzYF?8Ga-OXiF}u(5XQdP9$1>_mS^MQ_TD@{5Yr#C@w@5tCL8#Nx>a;Hr?3f!#PPEf zz@QqUztbWCllogPX)=MO$%kvw?|~0XV;)W_nMCT(&gZsk0>$QYlQ`I1I=j7Pq~^K6 zxf2psv~w(Lh?+qI2>QL~qTJtO)R>$XWl4yGBb z>eujGwaM0@=f=fYo z{j293Tf^&6eh3ahIFJ z0VTqOyLaeB$map(uzi3)@&ML)YylIX|6vV07>K-=uBL%$dNusiO`$}}3k3>NzsiY^hjT(3-QWiaZdt8RANLzUR zqzq3Bem1+FY#>j+ej0CKtE15GP6jVU(FifB)?gBoB^s^G zLdf)S53?;Lv!XYN7CY8)`WjC4k}=lU$#P{znUfIRCp^fOku|Ol9^&cEpr&<~%fWwL zmaps#N3_>h*QjZI`)SUwaXY)&-sl9#PtA=P)gWZ@ha1s5YIJs~UZ52QdcQwyj`w9Z z&BtztS_Ga?yc}>0VYteWI?^`)qp}hyp3DN{pi8pI)IsF%#O;l?CtLUUSkDGmJe7k< z)!z@L?rBl^AgaznXhw9nNZM>KC7@I5H-iEcJuN5CUMcGd7o6oluBO5_ZQ65Z?6r6Q zII^Y5m17Qs@)3Rd)Wee5%Tw7<|<2J(o$4}{%AOO3JAl& zMoUmViIvJxf&6314N zkm(pM7nb~U0-`7yeoBR za%2_+Rrn-D;#K*&ot%vVKEeX7UyOxgVa80fAaBcmF|M1`w!-_Z(0f{y< zyHgYzTB@|ONR>kYQLFu%_ZN)abVLqQV*nHj0K{9Ln${KAYk2NwmkSQ@op;OYM(elM+ZXy~yGDY_PyW0zAp|VuUKk$R|<)gWr zv>lqPTaU}%mI#@FC-L&Gf)HmiN7g_D@n-7ixt)I(}r3N(;cf%Ia%HO+H{Ws zQnKhHFaG?QdtC84uwE4(h2r)qm$jlaldcEFUEB9~O40tP5-1N6u!rjB|4#iS3wMjc ztwO}eI3fWRy;<;)O}TBOD@Q<2uUWQ_)il4a3(QA#l%Micloe%G{sQx$fE_nZxi<8QDmW%l!Iw~E@EqIa+$c4G|pT?XbIJ>qa09CK%72rf;1^03711+6TVkTzMn>ddlo3*W@r&#RJBY3{s!00j;VX>jz&ct7HX}Z4 ziu@Rt!_iBq=_e1W1LdL#!_KYBTF`Wd^Qd1{2}q{FGNVgyN>Qdj4*Q`0Ul@o;s(~rw zjw8Rlj;5hKpg|`w4^EY+EivK&7bVk3bly)P&U$nxgy+>%<(k0GprN;C&ukMxOxLOL zzkpj)C%yALKO$v_aJ_Q{G! z971(f!i$!_a_Bb(ElEpSo$|G zuHJsLf`bn|4V;qASdM)Ph;J&$3JqShCnEP2rjUzD$}$8ik~~7X;IN}BR8y6WWfnow z6K7MaL%2w&u}6H*?nw~ku99aNm)0Trmz|@bJIYzo_@E-GC?env!j0apMh>{v-ndm1 z1>z`6z}^mz*O5Up6!Hg(Nkp`HLwVrH62LgG556Lu7@+&?KBx=l>@q;*3Ky5|bb?pb zFRJ0|ahNPVx&2QUY|f2dtoFV=xPZ<@WFjIuyd|Y`NL&f5)!T!k3tjg3TSE`qmrcT;#k^`>7s$3x!x>UqcxApeGB6PLf zn}bcKM2LlzgPYHB5S!Dh?FTS3spGup$9D<-IhoH}1e}U1M!%SP)u5*V_?10>;T1Ph zfAmy`v*q55T%gYUjpo=*`Wg?=)17Ic43v5u9wLf9Jq=6@UhF9n--{@FL8!ZD@P=5g zH9BG-9*2BI_FyXiFCAFMV7ESwrTo(>*5XY84c|APT1F5@&|PIV2qq# zyXz(()RdT6WB^z-chAG}Dy`#&6&NJcw!_L!Z{|V2Jpor_?tKT)Gj!N>*Z2w0AM}TtLjq(PIPYDD)kaLK`CPV3bCA` zNY!s4P2Kw#XbF9v_G-{I#gbi>oq&R#P|Qz6PvlLi4&a18rLQzY@NVJ=hsmIuFlu~I zhyfabc%srn!bYVByoV%F-5resI@HxFnT2g;+q7N}A8=9<55D9P`ZKn9KRn_%e%Ze9T%f@Mg^ zKYx-HcCq#AYFq^Fo*Tx)gpqoiS8KmG3~=2yei6~@y&IWcqNa7B@4ch?>4 z5&`gX>I4`(G;A)A0Xka!uZGkz!{Mx1^MlI4Y1zy> z&D}3V%eE6twGROVt?M8%b-~B0hP2{F1i2Y@$-{ivd(%k!e&wO3ZaK$%1T8VHxj zqp(Kc-+T$#NJ4q*TY+_D#EC_d+7QN})G%F8+h9Mzct+xB;GxeY!ep&8A8fHnhA>kB|iFIa`t zFJcFQiRt4+8rLfe0HXM8M6NUkwtTI5k$aVl7{Ke?Lv+c=IC!GOZbVPXA?ENnZ3#d( zj{x{!4oERz*-m$ReDNFD1xo2x6PDNJS%f4!x;tle?jvp0x$3%0LNQ)RHF2&|yl4#nBNPFV9i!(PrV3M-H?nT9Ksbt^B z#l|2Hz<%H@OC?p}IHx*`($I0HzIRrPz_>9kvl^V{hLm9{mizU&m=3;&)nUeq zt9Np@Mn))Htu2hTDO4z)>P?R(&{7Q15AuP89?kC8o0q>t?!Anp6rT`!i|)*?B_W;r zi2f%F`>J^T(M0r$?zh8OjsI9Up?Y=d#gf49mq%;$QC$cxdR~=jQ`63@7<_SyN;~9B zu@BwG2aX%w<7vqY{ys;`w z(Aw~RL^c@;lsMyl86Tcu|XsB5H6ZQXs@H7MK(6 zbWwE`U1V~>Etn5>bo#*VxY7?)`IjhnY3*{1vV}4UHH?0YJhm6rsdz8#?BMQtwLa5E zoh}Tgt_YG1x_B{}z!vyp$7%8`i$gWUTHe%Vizz2X2pnq_Ya}(Iw`Q9uNOZAU+3VT! zi`Q0H{dMX7i5(`!_z2pU=sPc@tvKs!wx19a|DJyUO~^N?92cRlt!k?--~ZYn=b_^` z8Fh*`9Fv_N&JcUVeIt33Dk+|qOay-3tqw75hjGI2E?VP81qu8>l*0;HE-b!?) zYaee*lK)P;QI`o)J3-`URzWzlsU`eQ4pOJB~%WCz~Tc!i~MNt%O8VtJj|c<@q| zZeU3LhSh=lnf}UK)vmqVdux2F9HN0!B!1!walRunKj(rXx>w^d)_{7)ZGVy&@w~8I zQK#R`=Gb(i(zIW5A1WMUJiGd7Hd-ZFd>zH_e!^s+os}sjJMLCgfZ&SsZM}N=Uj(Hi z+Wut?=zaFz4ZS|L?H!5(q6>j@BtTYosu{~xj6C*mFM;t~KN{6>`<(nhS^0jXhnQHV zvEtX`H5MAQ5K661 z_|tn}s_(`;M{RLD$aOqW?Fb8FY@Av_Raw4)8k~s8_97hF%l*PlW6Hs`K}nI=!>_-w zFez$9`qb}bTgY6znjq0#NI9Kzno)LJm^r@9nMsLVfAw(?`o;rnKE~=?pHuBJghT#Z zC4Sm8s*{wgu2*E+X=eoFT`w`KPs5gjg(BP=TF+H^zj$I<7?iReP1nC(@CzNEWrz!= z92(y=f98WkET%au{@5<;=V`ARjw@l^S}2j9JgzFB%xvef(QO_ozvjoZ|>?*cjy63d%T9VoG&Q*Bp2Bso*Fg0?txIANbF zOlJCdIR~bioctQ1w%OIqwOyL+#9TNXDFbXFIIo~=e>Al1;_NA6wq?tQ#og}RT?sd_ z-CydM_lI9mtRy*zOx#+I3GQJhL2UCOpoGT=3Yk-({X`M!+zp#5$ z*x~o{Xo;oRIk@zp)pV4Xzx3`-bRD;E%gK=vl|K1}nhba2<#T1HFJ8oG-mjlYZlMqk zZ>q6d$(S|hI0VA?Dj6&Y61vWU81LZc_2=f+twWSsm$H||qDCck##5h$6>;Nx|6=yq zU0FOxH*n3yBPnx(?|SvClNW{Hl*nFDFA|MR>D*ys_dZeQ$DYkTTN0Ibm)78hV2tc2 z*|&TrqFo3bdUk4b)MDCh?S65AAtTwmC@Yxo>FkB+5Cbs+9odEcj$a>laP9uN_E>kL zkWQvS3|!~{{r(k410SQ{;i34q!DF14l^#eKixa2w7Ye>Z+=EidZ0$1;CD5aEXXc{B z_VU1u8d`S~5kzZ>R|aA+*a`xiYv2Z5c4Zk8&~#)W_MMNQp5+b+G5YL!bfwT7or^8R zq%89|0CK2Qlm;!GGq-$nGkC;AiKk6*AlP zk6D;Cpm9qLm9QV*7Tku4cE`+zRM=uBI_KNol1&dQAo>c&b2EC>5C)GjgFgYA?|965 zw31ckbHsba)2Ve4lY!StPBCpX@J$bH!_Ud(!xuFWuyN-1_?`Dlm2ENtr6EeS|hW4oUsHIr& z5mLx^!xvs~f1?I3CA{&Zlygno{)N3rpdQ^5;Jkz9%CPi{f%M_y&dooH4l`RZXJ*;# z6>1SA^=OrcAo9WxDm`UJJvFD6Kn*YDsQu9Lp=nEegCJqOuw}{HlikU_bO)wn+EDhN zMk3H3EJ3`^{N6CsZ5Q@1g9F82+Sg4RXO(P?)tgeuXP$&XY%*&N82R^}wo7?~iae#M z{XOz74WA9thR7a8vXD#2-Z~ak_Gqq2)9z;YpWEqtMD-f+lE5utKYPG)mb@An;=V&p zC#9`{;A5j(pdQB}F(bKPN=i0dvy_;Ez=@HcHqV$N|WD%yUlGug?!)1Xv8 z!U1B)x>jgkn5y-FHEL~<@xo%OxiCGyH+S2G+V~3>Y!lpnX#BxdKY8{Ztz}zAtP{Qxl5?)J3FjW!hikghx_<9#}2GkhJ1TWQmdCv1|I|sSgT_9 z$JctR#B&lTVr9~pijKcgpAcZSSJ+EAnWOPTQN{MxbwqD;jK#!L_q3%r<4H>jx8ru{ z=J#bBLy;QS+9McZU&WR9O{1S+A2})-ECiFzyp;tfljeQ3%zq>!MmeAJWUjlz`{3`7 z0wm6qMK9#@Asw+hhhlZ9@0aJl8Kjc$en`>44lr@DMPCWWisaSm9MdAwl#fGN;N+=7 zh$+D#{Z^mlckyf}LBg*A?b_<(QQ|^3-!@acDzS*=@@}R{QyPNlFQxor1r{2l3%%M0S zT1xQrg4xjE(A9H4r(357Z<)DalWK0*RW&|*%UEZfC|0t`b^q&q@XV(->pT|kS8KlI zK+_J!w=)fF$G{_T8wZfNT*Zdw)R1*y%dO&~d?dKEc!qY~969%Mj_5BPlUIGD zoFu0n&*zA+=_aShFj-ee_GD4ypjbumz><$`iFWbuZKnhn`#OSAxXq^N!+QStYvatw@nH*GRuO+~ZTyMXfoFsb}z0nbvs5Gc(Zzs`uTU`0| z!-q0$<5_0Xd;{P0njx)>7ammG7Hb3}LAzR}ey*-#jFBG^7COFHmz2Cge>o zImYC7tgD7z?|07M*Xtwn>Np3e2g}eYPX$6d9vUFxQ9NYDZda#Oe1#m>iwIcOZGv}C z{IZusv~SUQ2^Y~`2vIvf<_As${=*#oW;}Zhb9>4Gt^axFqs|?4l5~P8aa;p#6;`GoL@@k_=tup4$tEc4(pUq;j@?MoeF3%)M?`zC^&5O^_9Uj%keEAg*USZK5Glhpq=cOkiyq0;_X1!7x^aVLJ|UGDVNZJDuiBE1LRC#ZD6Ve@a&-m zvg$13&6QDu+#)qc!EcdVRR#9QeIKvX?>iwv>EuO&VfNyB?C>}nFDZgl^=|EiavJnX z286E8INi$+^huaUZAH>MJR3cQ`kzf{yb#~Sg(wk@0<$*@vAUHasP19+LitB9NGCw& zm4XDgfFIMP*wjOF+P~`6pfG3QXV>Jc6hBbYqnk5#v!WJ`C%LARsjuc_oy#Oq@>w4A zj=EgXZGw6AZFE}*JuihQDy8{fE&Wbh{5&#fX@Ad=e>8Be14Tge+Y17b%-EXFoqs!LSMxzHKgoCDE$ zM_)dGQ_9>n$#JP`Y3@8kEf~Am=E&r)3y!-u5Z|hyfF$Qax6#|v?Rw(r{ZsuukQ-{& zFDV?LC(6U>247QopWR)evOJ~v(nz*L^{E`9q``gd91Wc4?iNJ!K}EZ%oDFSc=vss=*+fD@wP4b)CBk5oxM*C)LE(L zM_!f7f+Cv>3yTnZ%hVZ9@(kNuI1w`c%kzB4%ydq`6x?KJ7Q%LMzGWL2<#~qR@?MB% zcpO(bJbZ87pA&uAW9e zl4@b+xjOE{4dqk*e{%)?!#e#}K>wq&^`E>Q8bJMbll=&Vq=lLYp3C^}JPSE|9Qsdl zb@pzjCI4AVfxgY9?B0KyQVv@GJD9=Mun!ViUY*@*rAtlO2Ug1e=kzsLKr=qjFS^q3 zJ^sfYDkqb+)!1fN+J4Rx-*yy&-ljr2Q~Q=?hHH}!nn;uHEH7PsLk{7D#g4+Ao1K}~ z36afP-87k#;}9L)>Ce&Iv1X;gkyI~kV%kz{h7BGImxkqT@LcQ@L-fNQ@s%@Q^+$y% z61ZX3b}YhNsGd&2b5377p+si0&u0EzXM_D0KZzK8%fMK#yokU?47Szh1HE&e12r0V zv|LGC>#Hw3G3g__ibMljJ?AvP;zXaUT_jXO3u|ikM!V5WLYHo466&~}V;_kXKO+!-BVN%TVu7u7EBl}UUI`^|A95iAn};_EX5|dP85`p0)lY$R9cAc4lJQ(_k<Qzw^-9!Q4OQbSy2B? zTQ$$@GkE#cfeUKS_)D)m&~pZVSEcFnO5FFF(1$vtQ{cUvg35+Evs6~iTYL%c;7hno zkVBmlP6~3)>T4PhmZhaEUBs5I-%>*~-76ZFFU!DSiq4iz4UMlo4)a_nld6i57`$GA z0=`TgkQz7nFmqDMwqS#o#L1^{CULfCg}*N5;@ESFh@2hT^>77aw5`DXuUq@5%yB{7 zRgNu7P;RmX&K4EQ>tyx>?OO+0wxP-D0JazWj|mTKz%8mCvTKx=IgXk57E-q}D$t&f z0}IBGV&lqD9G%UUHU)aNEnM``KGjWzQAr0<_dxAFROMQlg|Q~(af7OqJ^yGG5z^nR zhn`$;9z^<@v%L(Ovvb)SV955aA1@L`=Ag*qE>H;%5g zn);wJ?iVBgOCtg^jHT=eS6CidHhDr`u5l;>L-W8=zRYSnI3m+L3S*4 z_}z4_#Y>~{ccJ=Ly4_=I5xvdv_vBP8IaPOguxGk)vVjk>4fcmax0fG0dGfQkN_Cdg zsD%S4S^5)lInw~bDdBLOFg)U!y-Cb(MYE5}hP+z+!Mrp?e!L9XAG)~!)-`!&=%LGz z$Jx@_4yCxGf_Of$F{dKM@rX^``LdQG1BbV19)cpXYz&mdW7*z#zeohW|lQ zP5`dH{UH}Tn@9eCg9i&U(3?J$>mg@TbUJ(~_2Cg2N%DgmN#Wad{6~xel8>{?sMRxC zsqOk~eHbzp-{Og>saG}&rW}l2yXS$-7u%A6A3=N0F1~z314`&JU2z&q<8GM;eXMie zF4xwbUKw0A)j zOHyoBaxsUxCf`hj7#MT0v359?Vqq5i`H2`0f%f1sA+TuKf2i5Su2GQ4TI$lfz!qJc zq54wE{JrmF2@fvkrKLBUi&d7wR*@*wtFNJQccn3AR}@nGOEy@PR65>ll6PE*{msgv zeED7Sl*s+OxovCaqPn;U5N{OGwEg|p1Dr;sCY53*|H6ephODS!ii~hc;1e$jwAcCh zD()EDS8lnZF17n({QHGiEGW5h4ZecY<&A$+J1&zJhszD`wXVX|i`J!%B?u^WkEdtu zl7k_G3A@6y6ZreeO?A!M&*CL}uq*tbUM-emA98&}*f>~KdI|1vclWD3T!;d{MmF?E z<@De4hd3j)Z6`ok&Z7nFr`EP(JWnlogi8u)e&en!<4xj~(dLj+iTPztrUC&HHOq_F zI`>}UyT+SB^`FtljUwDZW(|F8lDqPqbM{fnmKztvh6Z->Z-%hXJT}r<8*Aev$vt7V z#r$dMvjggX_r6XTQb|5!bgtsMmL}gC5eM3XxAkJ9vXsHWnwU|(KqJH9rcKJ4b%rgj z8_qgBscO7jOj5Pd3*N-zcsIB!#KcJ|Be|xkc%Wh_T(`1voxlr4KcBHFo!X_f=m&N= zBa*y(e&c1@!Aq1{b+{iP#Hy|R;MGSi-7rF!f1r7u22J+G6OZe(O4sT395mS_$ljof zCcMdFkwKR))4KTBo41;$}L`HwemOq>t=$&C-bpe=A|I<#aXyX607++>cwZ=m-wbJHB%go&rRV#rPx9NQv`=y^=UqPrTsyRU0n7AB{ueOCdV_X zP)8cupLm4zU~nit_73fq(Z{Lz5k(f(+AD{T6eZGNVK-npay(Fkj2CY9y701lTBy_A z{TckK_UqUvuRH)6a876;D2%wbrNet;%M*{o(BWS%L|Q%&W&Iza7igluzyD;| zTCU&iW-#nnzQuy16Z|f2i*jK;S}g zh;O4l;(5hufbuKPtVHJHHUxB2;1i{!*1=B!_&5+U^Q|HpT`jDenudb{QHW$F_KfG| z;v8*~DvYjwTSJ!efr2FSaVVu=%+baP#S$P!x$tb;#e*y59Ar-}H_qX!OxqC7#D>j6 zkeovT03yVDeyCGOjBs?BS<;6G3Xq)SfI7N4Hed&i6z%r8y9hlO?;?yzpvME3uURZs z(CRL(LlYI;h--$mv)9Nhox(EnU^AcdH<{@lZ$x%%XRv{p*dMHim>gcwY!06V^mi4G{TkN-E9l%w?7f}>icEU} zKI)d{K;orl67f42$n=I?W>GWN(OMCK_o&-!L^S6W2EJ)Xx@mrV&R&POZWTnl#zk%1@{1~OG}J1vT;7@3HIGkb`w`{R&>jwT^qVU zrG5GKGE9W1yGyz=C0+>(bS~D zR4@87^fvF6`BZS9&qoEl-+3VJ!v?SrzMAR|29&p-%o%~QE$t<-IM&svKsxxS75ni!W4ZRD zRsDRFeuC;JHq@J`VM!-MdvocD;`KdI%3pQc-DNIBOy<-o3;SxYimC3?&z?6!jhb?@ z2mXcUW#!b+gykNedntsG#w?o~i2tkfVb!%h+IzabZpi>*C7yWKdqAb5bR|?I(_{?6 z4Bdx~$F3B-l6sC0e>!FeEB{t7^_{(#9GptRTgW@JT%^~FpwG?!lsDdm?OyW~pawHZ zh}RX?4-UxZ-bon}gb-*&0p$x=j~YQ$$_uP8yRxx&;3c4@r5uL&@>k)I&wiIah+ern z^w+0;=FTU0ll3?2aL>@x9sE#8S?|V1YI_(7m*j%hBKrMRs5Bdxw5C>2FL(;(hIdcm?iuQ=*ua*N|lnm0Ue~>!Uq-~b%KHQJq;e& z{RM|`i4P8MsXbz}NpH6iiZgr-qv-~#&<6Xza7S?ZAd&X>*4iV}J63Fi*9+;74E7=6 zt@8LwaN4&MM9T=hjOJ~>3hv<)U;Uj`bwv9qfBz1GsQ_MzOIO?n-00)NpWe@d))(y1 zs-NK&Lg;ZDQ(R{yk#<_*JkUG6FRpwQgHL)-@vK#NqB%?~w=(%|>(xz8Pa}D0?#ZSg zJ&MqNNh4%FQa6s00gIyQ!WyZ^$Z&^P`9{g3HkIP~G8_Q{JsA)e~3NOWiL|V%O zeAU+f94bMy#GBUvXD}2v=BR;)(Eic}ETJ`%l$U$c=IbwfKG!!kDXiugURuI-_5el$ zm4FHk9mVN_g6>l9eqS>Hq@3EiT1SA0yv+_)1%H7C^@A!?({_l_Bzm=#7pFt&=cNpc zi1G)5ocZ9@HPW{JvmCO2aa<-Sgthq8>g8*tB<9IyijPG2G)BsGg}=XVC)H zRPuKSOKK;d0+j-I@23y{||Z^yfa+$YS%3LXp11(SDg z0Vi>~q<7g)uijrONINL4owxWe6|}#$=lm2ooquvWL0Dx8W~XKT7Q?^BNa$sXEEPTo z!wGDv633gQRufye;k{$q088G5n?f7`iJ_f69aN$bdhDQ(3o4IVKp`ceQ1V<^q1M(v zc>kY+^=fiR?ly?D`vz`5Ue$!wE8j#~XqDeY<8Kh#5gCme_Yoe)olvz(@W(=-e-VL%bh`LjK}Vv+i8iIWcHFCo`MBE#dwh1 zSHTp>Y=PD%Vu~8j&|<}-dg2l~^&GI|i6^k06)lW!=3J9Qx|#T;WY^-5!=ye8Z!BC$ zt!MfvE1;IRLx9S4|I~PlEe{fgKH9I;#@$4QmoG#JkbB091s7!_^lD?5AiU<;x08u@ z`48^){x`}uXrY#Sy(<()NUkqnH^x26TyRkYm{Z(GvUKlv|H5T+z6gI+3Bs4a-t`w~?q8A;Tv|vXq4(OR4R>lbKv1e?S{>t0{mad5h%27Fp?_Z#4%GPjI5Urqt!E=r)28+nY zJAz4siX9aoF^Lo?HKO?fEvBD^>30RXR;y+JFC&b}M+=%`Fr~W;=~4md(kTLto3VO} z(?wq_3rFG1Nr242HKH}%zV%J+M-hyT+L@c+*ZRYiCI__7yl?$+6}t_?@oYKr-O1{? ziaq*|S1Gu&;KNW1#3%9a9Yy-wpW#b*mhjBQWr*7@qw18tleV8bbN2NN=Kr<%~G5Lo}YU)qiUEbhvcw8Z6~3bOuOq zU6O-7BiJUCt#9=DN`8mp*!J&boZcDF!?Q#*0`-ei*6tA!ph`@QZ^o-0U#i+~QxgM0 zN2W9P47rdf{q$TM3Brv{91%XN=MEhs zAot$j-0u2`i?F)JN~*=%M0p}8hQ|v`h0?29<={7w$N z(kXsaFJv-X*i)hbjs*HG%m1e7GQqA&H*vM=GeNW=+eUpUTRbMb`4KD#@{Qsc=7@-j zF1!iZt?mw&VJRlI3}0}9rC(zuE_zb@2~YXXdApMHcc|Pb;7OvLkJrR_@}rNn>a*zo ff4@2kCT5RP)bwga`gG@T`VQ$I*`Kw~{=)wSi%+FA literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs index 2be1dfd7a..17070fbc1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -10,6 +10,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search public const string FileImagePath = "Images\\file.png"; public const string DeleteFileFolderImagePath = "Images\\deletefilefolder.png"; public const string CopyImagePath = "Images\\copy.png"; + public const string IndexImagePath = "Images\\index.png"; public const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory"; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 31697aa95..0faae15b5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -71,11 +71,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo if (fileSystemInfo is System.IO.DirectoryInfo) { - folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, fileSystemInfo.FullName, query)); + folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, fileSystemInfo.FullName, query, true, false)); } else { - fileList.Add(ResultManager.CreateFileResult(fileSystemInfo.FullName, query)); + fileList.Add(ResultManager.CreateFileResult(fileSystemInfo.FullName, query, true, false)); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index ef392f920..50791251f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -11,7 +11,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { internal static class ResultManager { - internal static Result CreateFolderResult(string title, string subtitle, string path, Query query) + internal static Result CreateFolderResult(string title, string subtitle, string path, Query query, bool showIndexState = false, bool windowsIndexed = false) { return new Result { @@ -41,7 +41,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search query.ActionKeyword + " " + changeTo); return false; }, - ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path } + ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path, ShowIndexState = showIndexState, WindowsIndexed = windowsIndexed } }; } @@ -85,7 +85,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search }; } - internal static Result CreateFileResult(string filePath, Query query) + internal static Result CreateFileResult(string filePath, Query query, bool showIndexState = false, bool windowsIndexed = false) { var result = new Result { @@ -106,7 +106,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return true; }, - ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath } + ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath, ShowIndexState = showIndexState, WindowsIndexed = windowsIndexed } }; return result; } @@ -116,6 +116,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search { public string FullPath { get; set; } public ResultType Type { get; set; } + + public bool WindowsIndexed { get; set; } + + public bool ShowIndexState { get; set; } } internal enum ResultType diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 4f0f8439a..c2513820b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -71,10 +71,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex private Result CreateResult(string filename, string path, string fileType, Query query) { if (fileType == "Directory") - return ResultManager.CreateFolderResult(filename, path, path, query); + return ResultManager.CreateFolderResult(filename, path, path, query, true, true); else { - return ResultManager.CreateFileResult(path, query); + return ResultManager.CreateFileResult(path, query, true, true); } } From 9bce1a593eda728b688006cce49f9340699189fd Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 07:38:41 +1000 Subject: [PATCH 088/142] Add body for WindowsIndex search all hotkey test --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 6007970ba..64d6a91f3 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -226,6 +226,22 @@ namespace Flow.Launcher.Test.Plugins $"Actual path string is {previousDirectoryPath} {Environment.NewLine}"); } - public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString() { } + [TestCase("c:\\NonExistentFolder\\>", "scope='file:c:\\NonExistentFolder'")] + [TestCase("c:\\NonExistentFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " + + "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " + + "scope='file:c:\\NonExistentFolder'")] + public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + //When + var resultString = queryConstructor.QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path); + + // Then + Assert.IsTrue(resultString == expectedString, + $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + + $"Actual string was: {resultString}{Environment.NewLine}"); + } } } From b5e9ec3a9f81cbcff99bba8211c2611e8b40863d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 07:41:14 +1000 Subject: [PATCH 089/142] Add search hotkeys to Constants --- .../Search/Constants.cs | 7 ++-- .../Search/WindowsIndex/QueryConstructor.cs | 32 ++++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs index 17070fbc1..20d8b8261 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text; @@ -12,6 +12,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search public const string CopyImagePath = "Images\\copy.png"; public const string IndexImagePath = "Images\\index.png"; - public const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory"; + internal static readonly char[] SpecialSearchChars = new char[] + { + '?', '*', '>' + }; } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index cbf896d26..8a6c0f798 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -51,20 +51,41 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex /// public string QueryWhereRestrictionsForTopLevelDirectorySearch(string path) { - var directoryWhereRestriction = $"directory='file:"; + var searchDepth = $"directory='file:"; + return QueryWhereRestrictionsFromLocationPath(path, searchDepth); + } + + /// + /// Set the required WHERE clause restriction to search all files and subfolders of a specified directory. + /// + public string QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(string path) + { + var searchDepth = $"scope='file:"; + + return QueryWhereRestrictionsFromLocationPath(path, searchDepth); + } + + private string QueryWhereRestrictionsFromLocationPath(string path, string searchDepth) + { if (path.EndsWith("\\")) - return directoryWhereRestriction + $"{path}'"; + return searchDepth + $"{path}'"; var indexOfSeparator = path.LastIndexOf('\\'); var itemName = path.Substring(indexOfSeparator + 1); + if (itemName.StartsWith('>')) + itemName = itemName.Substring(1); + var previousLevelDirectory = path.Substring(0, indexOfSeparator); + if (string.IsNullOrEmpty(itemName)) + return searchDepth + $"{previousLevelDirectory}'"; + return $"(System.FileName LIKE '{itemName}%' " + $"OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND " + - directoryWhereRestriction + $"{previousLevelDirectory}'"; + searchDepth + $"{previousLevelDirectory}'"; } /// @@ -74,9 +95,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { string query = "SELECT TOP " + _settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; - query += QueryWhereRestrictionsForTopLevelDirectorySearch(path); + if (path.IndexOfAny(Constants.SpecialSearchChars) >= 0) + return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path); - return query; + return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path); } /// From 03bcef56e2af166711459fd3b7eea0498a19191e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 07:41:41 +1000 Subject: [PATCH 090/142] Change Constants class visibility to internal --- .../Search/Constants.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs index 20d8b8261..2953e4329 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -1,16 +1,18 @@ -using System; +using System; using System.Collections.Generic; using System.Text; namespace Flow.Launcher.Plugin.Explorer.Search { - public static class Constants + internal static class Constants { - public const string FolderImagePath = "Images\\folder.png"; - public const string FileImagePath = "Images\\file.png"; - public const string DeleteFileFolderImagePath = "Images\\deletefilefolder.png"; - public const string CopyImagePath = "Images\\copy.png"; - public const string IndexImagePath = "Images\\index.png"; + internal const string FolderImagePath = "Images\\folder.png"; + internal const string FileImagePath = "Images\\file.png"; + internal const string DeleteFileFolderImagePath = "Images\\deletefilefolder.png"; + internal const string CopyImagePath = "Images\\copy.png"; + internal const string IndexImagePath = "Images\\index.png"; + + internal const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory"; internal static readonly char[] SpecialSearchChars = new char[] { From 64986b609f15393f890929f31d57c7070d65bf49 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 08:06:49 +1000 Subject: [PATCH 091/142] rename test parameter variables --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 64d6a91f3..d662eb6f8 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -41,7 +41,7 @@ namespace Flow.Launcher.Test.Plugins private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false; - [TestCase("C:\\Dropbox", "directory='file:C:\\Dropbox'")] + [TestCase("C:\\SomeFolder", "directory='file:C:\\SomeFolder'")] public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString) { // Given @@ -57,7 +57,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual: {result}{Environment.NewLine}"); } - [TestCase("C:\\Dropbox", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\Dropbox'")] + [TestCase("C:\\SomeFolder", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder'")] public void GivenWindowsIndexSearch_WhenSearchTypeIsSearchTopFolderLevel_ThenQueryShouldUseExpectedString(string folderPath, string expectedString) { // Given @@ -75,7 +75,7 @@ namespace Flow.Launcher.Test.Plugins [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\", \"System.ItemType\" " + "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" + - " AND directory='file:C:\\Dropbox'")] + " AND directory='file:C:\\SomeFolder'")] public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString( string userSearchString, string expectedString) { @@ -91,9 +91,9 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {queryString}{Environment.NewLine}"); } - [TestCase("C:\\Dropbox\\App", "(System.FileName LIKE 'App%' " + - "OR CONTAINS(System.FileName,'\"App*\"',1033))" + - " AND directory='file:C:\\Dropbox'")] + [TestCase("C:\\SomeFolder\\SomeApp", "(System.FileName LIKE 'SomeApp%' " + + "OR CONTAINS(System.FileName,'\"SomeApp*\"',1033))" + + " AND directory='file:C:\\SomeFolder'")] public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryWhereRestrictionsShouldUseDirectoryString( string userSearchString, string expectedString) { @@ -200,9 +200,9 @@ namespace Flow.Launcher.Test.Plugins } - [TestCase(@"C:\Dropbox\Drop", true, @"C:\Dropbox\")] - [TestCase(@"C:\Dropbox\Drop\App", true, @"C:\Dropbox\Drop\")] - [TestCase(@"C:\Dropbox\Drop", false, "")] + [TestCase(@"C:\SomeFolder\SomeApp", true, @"C:\SomeFolder\")] + [TestCase(@"C:\SomeFolder\SomeApp\SomeFile", true, @"C:\SomeFolder\SomeApp\")] + [TestCase(@"C:\NonExistentFolder\SomeApp", false, "")] public void GivenAPartialPath_WhenPreviousLevelDirectoryExists_ThenShouldReturnThePreviousDirectoryPathString( string path, bool previousDirectoryExists, string expectedString) { @@ -226,10 +226,10 @@ namespace Flow.Launcher.Test.Plugins $"Actual path string is {previousDirectoryPath} {Environment.NewLine}"); } - [TestCase("c:\\NonExistentFolder\\>", "scope='file:c:\\NonExistentFolder'")] - [TestCase("c:\\NonExistentFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " + + [TestCase("c:\\SomeFolder\\>", "scope='file:c:\\SomeFolder'")] + [TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " + "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " + - "scope='file:c:\\NonExistentFolder'")] + "scope='file:c:\\SomeFolder'")] public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString) { // Given From a478e203522af879a3177cfbaf010d87dd3404a3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 08:55:41 +1000 Subject: [PATCH 092/142] Add test for DirectoryInfoSearch search criteria --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index d662eb6f8..048ed3963 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -1,6 +1,7 @@ using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.Explorer; using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; @@ -243,5 +244,22 @@ namespace Flow.Launcher.Test.Plugins $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + $"Actual string was: {resultString}{Environment.NewLine}"); } + + [TestCase("c:\\somefolder\\>somefile","*somefile*")] + [TestCase("c:\\somefolder\\somefile", "somefile*")] + [TestCase("c:\\somefolder\\", "*")] + public void GivenDirectoryInfoSearch_WhenSearchPatternHotKeyIsSearchAll_ThenSearchCriteriaShouldUseCriteriaString(string path, string expectedString) + { + // Given + var criteriaConstructor = new DirectoryInfoSearch(new Settings()); + + //When + var resultString = criteriaConstructor.ConstructSearchCriteria(path); + + // Then + Assert.IsTrue(resultString == expectedString, + $"Expected criteria string: {expectedString}{Environment.NewLine} " + + $"Actual criteria string was: {resultString}{Environment.NewLine}"); + } } } From ddffa52acaae8e83e5820c68edbd1b94674ed151 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 08:56:55 +1000 Subject: [PATCH 093/142] Add method to construct search criteria for DirectoryInfoSeach --- .../DirectoryInfo/DirectoryInfoSearch.cs | 54 ++++++++++--------- .../Search/SearchManager.cs | 2 +- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 0faae15b5..4ed63b7c3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -10,40 +10,26 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { public class DirectoryInfoSearch { - private PluginInitContext _context; - private Settings _settings; - public DirectoryInfoSearch(Settings settings, PluginInitContext context) + public DirectoryInfoSearch(Settings settings) { - _context = context; _settings = settings; } - internal List DirectoryAllFilesFoldersSearch(Query query, string search) - { - return DirectorySearch(SearchOption.AllDirectories, query, search); - } - internal List TopLevelDirectorySearch(Query query, string search) { - return DirectorySearch(SearchOption.TopDirectoryOnly, query, search); + var criteria = ConstructSearchCriteria(search); + + if (search.IndexOfAny(Constants.SpecialSearchChars) >= 0) + return DirectorySearch(SearchOption.AllDirectories, query, search, criteria); + + return DirectorySearch(SearchOption.TopDirectoryOnly, query, search, criteria); } - private List DirectorySearch(SearchOption searchOption, Query query, string search) + public string ConstructSearchCriteria(string search) { - var results = new List(); - //var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0; string incompleteName = ""; - //if (hasSpecial || !Directory.Exists(search + "\\")) - //// give the ability to search all folder when starting with > - //if (incompleteName.StartsWith(">")) - //{ - // searchOption = SearchOption.AllDirectories; - - // // match everything before and after search term using supported wildcard '*', ie. *searchterm* - // incompleteName = "*" + incompleteName.Substring(1); - //} if (!search.EndsWith("\\")) { @@ -52,18 +38,36 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo incompleteName = search.Substring(indexOfSeparator + 1).ToLower(); - search = search.Substring(0, indexOfSeparator + 1); + if (incompleteName.StartsWith(">")) + incompleteName = "*" + incompleteName.Substring(1); } incompleteName += "*"; + return incompleteName; + } + + private List DirectorySearch(SearchOption searchOption, Query query, string search, string searchCriteria) + { + var results = new List(); + + var path = search; + + if (!search.EndsWith("\\")) + { + // not full path, get previous level directory string + var indexOfSeparator = search.LastIndexOf('\\'); + + path = search.Substring(0, indexOfSeparator + 1); + } + var folderList = new List(); var fileList = new List(); try { - var directoryInfo = new System.IO.DirectoryInfo(search); - var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption); + var directoryInfo = new System.IO.DirectoryInfo(path); + var fileSystemInfos = directoryInfo.GetFileSystemInfos(searchCriteria, searchOption); foreach (var fileSystemInfo in fileSystemInfos) { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 1bca58366..f4d64157c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -66,7 +66,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search private List DirectoryInfoClassSearch(Query query, string querySearch) { - var directoryInfoSearch = new DirectoryInfoSearch(_settings, _context); + var directoryInfoSearch = new DirectoryInfoSearch(_settings); return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch); } From c7642b73c0c8a92e3128da3c9f3cd367de4ce403 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 18:23:00 +1000 Subject: [PATCH 094/142] update tests --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 048ed3963..508468ea1 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -42,7 +42,7 @@ namespace Flow.Launcher.Test.Plugins private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false; - [TestCase("C:\\SomeFolder", "directory='file:C:\\SomeFolder'")] + [TestCase("C:\\SomeFolder\\", "directory='file:C:\\SomeFolder\\'")] public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString) { // Given @@ -58,8 +58,9 @@ namespace Flow.Launcher.Test.Plugins $"Actual: {result}{Environment.NewLine}"); } - [TestCase("C:\\SomeFolder", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder'")] - public void GivenWindowsIndexSearch_WhenSearchTypeIsSearchTopFolderLevel_ThenQueryShouldUseExpectedString(string folderPath, string expectedString) + [TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\'")] + [TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\'")] + public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString) { // Given var queryConstructor = new QueryConstructor(new Settings()); @@ -73,8 +74,8 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {queryString}{Environment.NewLine}"); } - [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\", \"System.ItemType\" " + - "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + + [TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType " + + "FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" + " AND directory='file:C:\\SomeFolder'")] public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString( @@ -188,8 +189,8 @@ namespace Flow.Launcher.Test.Plugins [TestCase(@"\c:\", false)] [TestCase(@"cc:\", false)] [TestCase(@"\\\SomeNetworkLocation\", false)] - [TestCase("RandomString", false)] - public void WhenGivenQuerySearchString_ThenShouldIndicateIfItIsLocationString(string querySearchString, bool expectedResult) + [TestCase("RandomFile", false)] + public void WhenGivenQuerySearchString_ThenShouldIndicateIfIsLocationPathString(string querySearchString, bool expectedResult) { // When, Given var result = FilesFolders.IsLocationPathString(querySearchString); From 0c53d76fec4db7248ebaa577b6e0c8a200a1fcb5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 21:33:18 +1000 Subject: [PATCH 095/142] fix TopLevelDirectory search activation --- .../SharedCommands/FilesFolders.cs | 18 ++++++++++++++++++ Flow.Launcher.Test/Plugins/ExplorerTest.cs | 13 +++++++++++++ .../DirectoryInfo/DirectoryInfoSearch.cs | 15 +++------------ .../Search/SearchManager.cs | 2 ++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index a81ff8008..c41eddceb 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -185,5 +185,23 @@ namespace Flow.Launcher.Plugin.SharedCommands return previousDirectoryPath; } + + /// + /// Gets the previous level directory from a path string. + /// Does not check that previous level directory exists and returns + /// passed in string if is complete path + /// + public static string GetPreviousLevelDirectoryIfPathIncomplete(string path) + { + if (!path.EndsWith("\\")) + { + // not full path, get previous level directory string + var indexOfSeparator = path.LastIndexOf('\\'); + + return path.Substring(0, indexOfSeparator + 1); + } + + return path; + } } } diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 508468ea1..7f049fe3e 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -228,6 +228,19 @@ namespace Flow.Launcher.Test.Plugins $"Actual path string is {previousDirectoryPath} {Environment.NewLine}"); } + [TestCase(@"C:\NonExistentFolder\SomeApp", @"C:\NonExistentFolder\")] + [TestCase(@"C:\NonExistentFolder\SomeApp\", @"C:\NonExistentFolder\SomeApp\")] + public void WhenGivenAPath_ThenShouldReturnThePreviousDirectoryPathIfIncompleteOrOriginalString( + string path, string expectedString) + { + var returnedPath = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(path); + + //Then + Assert.IsTrue(returnedPath == expectedString, + $"Expected path string: {expectedString} {Environment.NewLine} " + + $"Actual path string is {returnedPath} {Environment.NewLine}"); + } + [TestCase("c:\\SomeFolder\\>", "scope='file:c:\\SomeFolder'")] [TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " + "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 4ed63b7c3..c535fef99 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { var criteria = ConstructSearchCriteria(search); - if (search.IndexOfAny(Constants.SpecialSearchChars) >= 0) + if (search.LastIndexOf('>') > search.LastIndexOf('\\')) return DirectorySearch(SearchOption.AllDirectories, query, search, criteria); return DirectorySearch(SearchOption.TopDirectoryOnly, query, search, criteria); @@ -33,12 +33,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo if (!search.EndsWith("\\")) { - // not full path, get previous level directory string var indexOfSeparator = search.LastIndexOf('\\'); incompleteName = search.Substring(indexOfSeparator + 1).ToLower(); - if (incompleteName.StartsWith(">")) + if (incompleteName.StartsWith('>')) incompleteName = "*" + incompleteName.Substring(1); } @@ -51,15 +50,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { var results = new List(); - var path = search; - - if (!search.EndsWith("\\")) - { - // not full path, get previous level directory string - var indexOfSeparator = search.LastIndexOf('\\'); - - path = search.Substring(0, indexOfSeparator + 1); - } + var path = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(search); var folderList = new List(); var fileList = new List(); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index f4d64157c..7bc038900 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -108,6 +108,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search private bool WindowsIndexExists(string path) { + path = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(path); + return _indexSearch.PathIsIndexed(path); } From 758b565f01288d1eefef6c8a476e54df9b631f22 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 28 May 2020 21:35:12 +1000 Subject: [PATCH 096/142] change strings to Constants --- Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs | 7 +++---- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 8 ++++---- .../Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs | 4 ++-- .../Search/WindowsIndex/QueryConstructor.cs | 8 ++++---- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs index 2953e4329..a299f24c3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -14,9 +14,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory"; - internal static readonly char[] SpecialSearchChars = new char[] - { - '?', '*', '>' - }; + internal const char AllFilesFolderSearchWildcard = '>'; + + internal const char DirectorySeperator = '\\'; } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index c535fef99..f68e8143e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { var criteria = ConstructSearchCriteria(search); - if (search.LastIndexOf('>') > search.LastIndexOf('\\')) + if (search.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > search.LastIndexOf(Constants.DirectorySeperator)) return DirectorySearch(SearchOption.AllDirectories, query, search, criteria); return DirectorySearch(SearchOption.TopDirectoryOnly, query, search, criteria); @@ -31,13 +31,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { string incompleteName = ""; - if (!search.EndsWith("\\")) + if (!search.EndsWith(Constants.DirectorySeperator)) { - var indexOfSeparator = search.LastIndexOf('\\'); + var indexOfSeparator = search.LastIndexOf(Constants.DirectorySeperator); incompleteName = search.Substring(indexOfSeparator + 1).ToLower(); - if (incompleteName.StartsWith('>')) + if (incompleteName.StartsWith(Constants.AllFilesFolderSearchWildcard)) incompleteName = "*" + incompleteName.Substring(1); } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 50791251f..ec9c793ac 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -35,7 +35,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } } - string changeTo = path.EndsWith("\\") ? path : path + "\\"; + string changeTo = path.EndsWith(Constants.DirectorySeperator) ? path : path + Constants.DirectorySeperator; Main.Context.API.ChangeQuery(string.IsNullOrEmpty(query.ActionKeyword) ? changeTo : query.ActionKeyword + " " + changeTo); @@ -56,7 +56,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } else { - folderName = folderName.TrimEnd('\\').Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + folderName = folderName.TrimEnd(Constants.DirectorySeperator).Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); } var firstResult = ""; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index 8a6c0f798..e037cc8c9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -68,14 +68,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex private string QueryWhereRestrictionsFromLocationPath(string path, string searchDepth) { - if (path.EndsWith("\\")) + if (path.EndsWith(Constants.DirectorySeperator)) return searchDepth + $"{path}'"; - var indexOfSeparator = path.LastIndexOf('\\'); + var indexOfSeparator = path.LastIndexOf(Constants.DirectorySeperator); var itemName = path.Substring(indexOfSeparator + 1); - if (itemName.StartsWith('>')) + if (itemName.StartsWith(Constants.AllFilesFolderSearchWildcard)) itemName = itemName.Substring(1); var previousLevelDirectory = path.Substring(0, indexOfSeparator); @@ -95,7 +95,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { string query = "SELECT TOP " + _settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; - if (path.IndexOfAny(Constants.SpecialSearchChars) >= 0) + if (path.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > path.LastIndexOf(Constants.DirectorySeperator)) return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path); return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path); From 5f13fd8a802f4a2154544efdaf1ca89a2ab82690 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 1 Jun 2020 05:52:01 +1000 Subject: [PATCH 097/142] add index info on Open Current Folder result --- Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs | 8 +++++--- .../Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs | 5 +++-- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 8 ++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 544b74abe..ab9c8f10f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -20,12 +20,14 @@ namespace Flow.Launcher.Plugin.Explorer if (record.Type == ResultType.File) { contextMenus.Add(CreateOpenWithEditorResult(record)); - contextMenus.Add(CreateOpenContainingFolderResult(record)); } + contextMenus.Add(CreateOpenContainingFolderResult(record)); + if (record.ShowIndexState) - contextMenus.Add(new Result { Title = "Indexed: " + (record.WindowsIndexed ? "Yes" : "No"), - Score = 501, IcoPath = Constants.IndexImagePath }); + contextMenus.Add(new Result {Title = "Indexed: " + (record.WindowsIndexed ? "Yes" : "No"), + SubTitle = "Location: " + record.FullPath, + Score = 501, IcoPath = Constants.IndexImagePath}); var icoPath = (record.Type == ResultType.File) ? Constants.FileImagePath : Constants.FolderImagePath; var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder"; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index ec9c793ac..d9035ea9d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -45,7 +45,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search }; } - internal static Result CreateOpenCurrentFolderResult(string path, bool isPreviousDirectoryLevel) + internal static Result CreateOpenCurrentFolderResult(string path, bool isPreviousDirectoryLevel, bool windowsIndexed = false) { var folderName = path; @@ -81,7 +81,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search { FilesFolders.OpenPath(path); return true; - } + }, + ContextData = new SearchResult { Type = ResultType.Folder, FullPath = folderName, ShowIndexState = true, WindowsIndexed = windowsIndexed } }; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 7bc038900..d6dec77d2 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -48,7 +48,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search var results = new List(); - var currentFolderResult = CreateOpenCurrentFolderResult(FilesFolders.LocationExists, querySearch); + var currentFolderResult = CreateOpenCurrentFolderResult(FilesFolders.LocationExists, querySearch, WindowsIndexExists(querySearch)); if (currentFolderResult == null) return new List(); @@ -113,17 +113,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search return _indexSearch.PathIsIndexed(path); } - private Result CreateOpenCurrentFolderResult(Func locationExists, string querySearchString) + private Result CreateOpenCurrentFolderResult(Func locationExists, string querySearchString, bool indexExists) { if (locationExists(querySearchString)) - return ResultManager.CreateOpenCurrentFolderResult(querySearchString, false); + return ResultManager.CreateOpenCurrentFolderResult(querySearchString, false, indexExists); var previousDirectoryPath = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, querySearchString); if (string.IsNullOrEmpty(previousDirectoryPath)) return null; - return ResultManager.CreateOpenCurrentFolderResult(previousDirectoryPath, true); + return ResultManager.CreateOpenCurrentFolderResult(previousDirectoryPath, true, indexExists); } } } From 1cea02a30027856517fc00f2a1ec63dec1c08d8a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 1 Jun 2020 07:53:34 +1000 Subject: [PATCH 098/142] mini refactor to check current or previous directory once --- .../Search/SearchManager.cs | 52 +++++++------------ 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index d6dec77d2..44a685427 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; @@ -35,31 +35,35 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!FilesFolders.IsLocationPathString(querySearch)) return WindowsIndexFilesAndFoldersSearch(query, querySearch); - if (EnvironmentVariables.IsEnvironmentVariableSearch(querySearch)) + var locationPath = query.Search; + + if (EnvironmentVariables.IsEnvironmentVariableSearch(locationPath)) { - return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query); + return EnvironmentVariables.GetEnvironmentStringPathSuggestions(locationPath, query); } // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ - if (querySearch.Substring(1).Contains("%")) + if (locationPath.Substring(1).Contains("%")) { - querySearch = EnvironmentVariables.TranslateEnvironmentVariablePath(querySearch); + locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath); } var results = new List(); - var currentFolderResult = CreateOpenCurrentFolderResult(FilesFolders.LocationExists, querySearch, WindowsIndexExists(querySearch)); + if (!FilesFolders.LocationExists(FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(locationPath))) + return results; - if (currentFolderResult == null) - return new List(); + var indexExists = _indexSearch.PathIsIndexed(locationPath); - results.Add(currentFolderResult); + results.Add(ResultManager.CreateOpenCurrentFolderResult(FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(locationPath), + true, + indexExists)); results.AddRange(TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, DirectoryInfoClassSearch, - WindowsIndexExists, + indexExists, query, - querySearch)); + locationPath)); return results; } @@ -101,29 +105,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search var queryConstructor = new QueryConstructor(_settings); return _indexSearch.WindowsIndexSearch(path, - queryConstructor.CreateQueryHelper().ConnectionString, - queryConstructor.QueryForTopLevelDirectorySearch, - query); - } - - private bool WindowsIndexExists(string path) - { - path = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(path); - - return _indexSearch.PathIsIndexed(path); - } - - private Result CreateOpenCurrentFolderResult(Func locationExists, string querySearchString, bool indexExists) - { - if (locationExists(querySearchString)) - return ResultManager.CreateOpenCurrentFolderResult(querySearchString, false, indexExists); - - var previousDirectoryPath = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, querySearchString); - - if (string.IsNullOrEmpty(previousDirectoryPath)) - return null; - - return ResultManager.CreateOpenCurrentFolderResult(previousDirectoryPath, true, indexExists); + queryConstructor.CreateQueryHelper().ConnectionString, + queryConstructor.QueryForTopLevelDirectorySearch, + query); } } } From f2690ebfe29796111995b89c07af2ea8da2a4807 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 1 Jun 2020 08:03:04 +1000 Subject: [PATCH 099/142] fix scenario windows index still returned indexed subfolder results despite top level is not indexed when searching for top level. eg. searching for c:\ --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 8 ++------ .../Search/SearchManager.cs | 10 ++++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 7f049fe3e..ca4e96a07 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -34,10 +34,6 @@ namespace Flow.Launcher.Test.Plugins }; } - private bool MethodIndexExistsReturnsTrue(string dummyString) => true; - - private bool MethodIndexExistsReturnsFalse(string dummyString) => false; - private bool PreviousLocationExistsReturnsTrue(string dummyString) => true; private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false; @@ -154,7 +150,7 @@ namespace Flow.Launcher.Test.Plugins var results = searchManager.TopLevelFolderSearchBehaviour( MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, - MethodIndexExistsReturnsFalse, + false, new Query(), "string not used"); @@ -174,7 +170,7 @@ namespace Flow.Launcher.Test.Plugins var results = searchManager.TopLevelFolderSearchBehaviour( MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, - MethodIndexExistsReturnsTrue, + true, new Query(), "string not used"); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 44a685427..cc6b5ce79 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; @@ -78,16 +78,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search public List TopLevelFolderSearchBehaviour( Func> windowsIndexSearch, Func> directoryInfoClassSearch, - Func indexExists, + bool indexExists, Query query, string querySearchString) { - var results = windowsIndexSearch(query, querySearchString); - - if (results.Count == 0 && !indexExists(querySearchString)) + if (!indexExists) return directoryInfoClassSearch(query, querySearchString); - return results; + return windowsIndexSearch(query, querySearchString); } private List WindowsIndexFilesAndFoldersSearch(Query query, string querySearchString) From cc3d7d7dedd06978f8351c5fb0093c208ef34cd2 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 1 Jun 2020 14:11:51 +1000 Subject: [PATCH 100/142] fix CreateOpenCurrentFolderResult incorrectly using foldername --- .../Search/ResultManager.cs | 44 +++++++++---------- .../Search/SearchManager.cs | 4 +- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index d9035ea9d..ac1588371 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -45,44 +45,44 @@ namespace Flow.Launcher.Plugin.Explorer.Search }; } - internal static Result CreateOpenCurrentFolderResult(string path, bool isPreviousDirectoryLevel, bool windowsIndexed = false) + internal static Result CreateOpenCurrentFolderResult(string path, bool windowsIndexed = false) { - var folderName = path; + var folderName = ""; - if (folderName.EndsWith(":\\")) + var title = "Open current directory"; + + var previousDirectoryPath = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(path); + + if (previousDirectoryPath != path) { - var driveLetter = folderName.Substring(0, 1).ToUpper(); - folderName = driveLetter + " drive"; - } - else - { - folderName = folderName.TrimEnd(Constants.DirectorySeperator).Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + if (previousDirectoryPath.EndsWith(":\\")) + { + var driveLetter = path.Substring(0, 1).ToUpper(); + folderName = driveLetter + " drive"; + } + else + { + folderName = previousDirectoryPath.TrimEnd(Constants.DirectorySeperator).Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + } + + title = "Open " + folderName; } - var firstResult = ""; - - if (isPreviousDirectoryLevel) - { - firstResult = "Open " + folderName; - } - else - { - firstResult = "Open current directory"; - } + var fullPath = previousDirectoryPath != path ? previousDirectoryPath : path; return new Result { - Title = firstResult, + Title = title, SubTitle = $"Use > to search files and subfolders within {folderName}, " + $"* to search for file extensions in {folderName} or both >* to combine the search", - IcoPath = path, + IcoPath = fullPath, Score = 500, Action = c => { FilesFolders.OpenPath(path); return true; }, - ContextData = new SearchResult { Type = ResultType.Folder, FullPath = folderName, ShowIndexState = true, WindowsIndexed = windowsIndexed } + ContextData = new SearchResult { Type = ResultType.Folder, FullPath = fullPath, ShowIndexState = true, WindowsIndexed = windowsIndexed } }; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index cc6b5ce79..f77da2eaa 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -55,9 +55,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search var indexExists = _indexSearch.PathIsIndexed(locationPath); - results.Add(ResultManager.CreateOpenCurrentFolderResult(FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(locationPath), - true, - indexExists)); + results.Add(ResultManager.CreateOpenCurrentFolderResult(locationPath, indexExists)); results.AddRange(TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, DirectoryInfoClassSearch, From dcca88d7462d1ec54a7b9ff8f61c8c38f32c405a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 1 Jun 2020 21:11:29 +1000 Subject: [PATCH 101/142] fix CreateOpenCurrentFolderResult's opening path and text display --- .../Search/ResultManager.cs | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index ac1588371..5995ad905 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -47,42 +47,41 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal static Result CreateOpenCurrentFolderResult(string path, bool windowsIndexed = false) { - var folderName = ""; + var retrievedDirectoryPath = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(path); + + var folderName = retrievedDirectoryPath.TrimEnd(Constants.DirectorySeperator).Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + + if (retrievedDirectoryPath.EndsWith(":\\")) + { + var driveLetter = path.Substring(0, 1).ToUpper(); + folderName = driveLetter + " drive"; + } var title = "Open current directory"; - var previousDirectoryPath = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(path); - - if (previousDirectoryPath != path) - { - if (previousDirectoryPath.EndsWith(":\\")) - { - var driveLetter = path.Substring(0, 1).ToUpper(); - folderName = driveLetter + " drive"; - } - else - { - folderName = previousDirectoryPath.TrimEnd(Constants.DirectorySeperator).Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); - } - + if (retrievedDirectoryPath != path) title = "Open " + folderName; - } - var fullPath = previousDirectoryPath != path ? previousDirectoryPath : path; + + var subtitleFolderName = folderName; + + // ie. max characters can be displayed without subtitle cutting off: "Program Files (x86)" + if (folderName.Length > 19) + subtitleFolderName = "directory"; return new Result { Title = title, - SubTitle = $"Use > to search files and subfolders within {folderName}, " + - $"* to search for file extensions in {folderName} or both >* to combine the search", - IcoPath = fullPath, + SubTitle = $"Use > to search within {subtitleFolderName}, " + + $"* to search for file extensions or >* to combine both searches.", + IcoPath = retrievedDirectoryPath, Score = 500, Action = c => { - FilesFolders.OpenPath(path); + FilesFolders.OpenPath(retrievedDirectoryPath); return true; }, - ContextData = new SearchResult { Type = ResultType.Folder, FullPath = fullPath, ShowIndexState = true, WindowsIndexed = windowsIndexed } + ContextData = new SearchResult { Type = ResultType.Folder, FullPath = retrievedDirectoryPath, ShowIndexState = true, WindowsIndexed = windowsIndexed } }; } From 4eb2fe9297688f0fadf060a2ca32afcb676ef228 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Jun 2020 13:26:33 +1000 Subject: [PATCH 102/142] fix typo --- Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 5995ad905..6da36af06 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -67,7 +67,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search // ie. max characters can be displayed without subtitle cutting off: "Program Files (x86)" if (folderName.Length > 19) - subtitleFolderName = "directory"; + subtitleFolderName = "the directory"; return new Result { From 76297786102900b6618bb430a6a67f6741cd0b2f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Jun 2020 20:04:22 +1000 Subject: [PATCH 103/142] rename top level directory search method and tests to reflect intent --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 8 ++++---- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index ca4e96a07..261dfd593 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -141,13 +141,13 @@ namespace Flow.Launcher.Test.Plugins } [TestCase] - public void GivenWindowsIndexSearch_WhenReturnedZeroResultsAndIsNotIndexed_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() + public void GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() { // Given var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When - var results = searchManager.TopLevelFolderSearchBehaviour( + var results = searchManager.TopLevelDirectorySearchBehaviour( MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, false, @@ -161,13 +161,13 @@ namespace Flow.Launcher.Test.Plugins } [TestCase] - public void GivenWindowsIndexSearch_WhenReturnedZeroResultsAndIsIndexed_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch() + public void GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch() { // Given var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When - var results = searchManager.TopLevelFolderSearchBehaviour( + var results = searchManager.TopLevelDirectorySearchBehaviour( MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, true, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index f77da2eaa..2a1c7676d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; @@ -57,8 +57,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search results.Add(ResultManager.CreateOpenCurrentFolderResult(locationPath, indexExists)); - results.AddRange(TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, - DirectoryInfoClassSearch, + results.AddRange(TopLevelDirectorySearchBehaviour(WindowsIndexTopLevelFolderSearch, + DirectoryInfoClassSearch, indexExists, query, locationPath)); @@ -73,7 +73,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch); } - public List TopLevelFolderSearchBehaviour( + public List TopLevelDirectorySearchBehaviour( Func> windowsIndexSearch, Func> directoryInfoClassSearch, bool indexExists, From 5c46464a4196dd188cda623f88f9a271137c9ff1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Jun 2020 20:12:14 +1000 Subject: [PATCH 104/142] add condition for using Windows Index Search --- .../Search/SearchManager.cs | 35 +++++++++++++------ .../Flow.Launcher.Plugin.Explorer/Settings.cs | 6 ++++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 2a1c7676d..6df71f39d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,9 +1,10 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; +using System.Linq; namespace Flow.Launcher.Plugin.Explorer.Search { @@ -53,15 +54,15 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!FilesFolders.LocationExists(FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(locationPath))) return results; - var indexExists = _indexSearch.PathIsIndexed(locationPath); + var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath); - results.Add(ResultManager.CreateOpenCurrentFolderResult(locationPath, indexExists)); + results.Add(ResultManager.CreateOpenCurrentFolderResult(locationPath, useIndexSearch)); results.AddRange(TopLevelDirectorySearchBehaviour(WindowsIndexTopLevelFolderSearch, DirectoryInfoClassSearch, - indexExists, - query, - locationPath)); + useIndexSearch, + query, + locationPath)); return results; } @@ -76,11 +77,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search public List TopLevelDirectorySearchBehaviour( Func> windowsIndexSearch, Func> directoryInfoClassSearch, - bool indexExists, + bool useIndexSearch, Query query, string querySearchString) { - if (!indexExists) + if (!useIndexSearch) return directoryInfoClassSearch(query, querySearchString); return windowsIndexSearch(query, querySearchString); @@ -91,9 +92,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search var queryConstructor = new QueryConstructor(_settings); return _indexSearch.WindowsIndexSearch(querySearchString, - queryConstructor.CreateQueryHelper().ConnectionString, - queryConstructor.QueryForAllFilesAndFolders, - query); + queryConstructor.CreateQueryHelper().ConnectionString, + queryConstructor.QueryForAllFilesAndFolders, + query); } private List WindowsIndexTopLevelFolderSearch(Query query, string path) @@ -105,5 +106,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search queryConstructor.QueryForTopLevelDirectorySearch, query); } + + private bool UseWindowsIndexForDirectorySearch(string locationPath) + { + if (!_settings.UseWindowsIndexForDirectorySearch) + return false; + + if (_settings.WindowsIndexExcludedDirectories + .Any(x => x.Path == FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(locationPath))) + return false; + + return _indexSearch.PathIsIndexed(locationPath); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index c8eb87c34..abe452481 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -12,5 +12,11 @@ namespace Flow.Launcher.Plugin.Explorer [JsonProperty] public List FolderLinks { get; set; } = new List(); + + [JsonProperty] + public bool UseWindowsIndexForDirectorySearch { get; set; } = true; + + [JsonProperty] + public List WindowsIndexExcludedDirectories { get; set; } = new List(); } } From 2225cbff9219cdbe6d823d374760054088e7e2fe Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Jun 2020 20:21:28 +1000 Subject: [PATCH 105/142] rename method to reflect behaviour --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 8 ++++---- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 2 +- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 2 +- .../Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs | 2 +- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index c41eddceb..32f6507fa 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -187,11 +187,11 @@ namespace Flow.Launcher.Plugin.SharedCommands } /// - /// Gets the previous level directory from a path string. - /// Does not check that previous level directory exists and returns - /// passed in string if is complete path + /// Returns the previous level directory if path incomplete. + /// Does not check if previous level directory exists. + /// Returns passed in string if is complete path /// - public static string GetPreviousLevelDirectoryIfPathIncomplete(string path) + public static string ReturnPreviousDirectoryIfIncompleteString(string path) { if (!path.EndsWith("\\")) { diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 261dfd593..ffdc1eed8 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -229,7 +229,7 @@ namespace Flow.Launcher.Test.Plugins public void WhenGivenAPath_ThenShouldReturnThePreviousDirectoryPathIfIncompleteOrOriginalString( string path, string expectedString) { - var returnedPath = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(path); + var returnedPath = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path); //Then Assert.IsTrue(returnedPath == expectedString, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index f68e8143e..f70c4d125 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -50,7 +50,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { var results = new List(); - var path = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(search); + var path = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(search); var folderList = new List(); var fileList = new List(); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 6da36af06..22b17a985 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -47,7 +47,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal static Result CreateOpenCurrentFolderResult(string path, bool windowsIndexed = false) { - var retrievedDirectoryPath = FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(path); + var retrievedDirectoryPath = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path); var folderName = retrievedDirectoryPath.TrimEnd(Constants.DirectorySeperator).Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 6df71f39d..33d255ae0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -51,7 +51,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search var results = new List(); - if (!FilesFolders.LocationExists(FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(locationPath))) + if (!FilesFolders.LocationExists(FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) return results; var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath); @@ -113,7 +113,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return false; if (_settings.WindowsIndexExcludedDirectories - .Any(x => x.Path == FilesFolders.GetPreviousLevelDirectoryIfPathIncomplete(locationPath))) + .Any(x => x.Path == FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) return false; return _indexSearch.PathIsIndexed(locationPath); From 6330a784e82a740ba765d719c553e27b4c92dd1a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Jun 2020 20:58:22 +1000 Subject: [PATCH 106/142] add context menu to exclude indexed path from using index search --- .../ContextMenu.cs | 23 +++++++++++++++--- .../Flow.Launcher.Plugin.Explorer.csproj | 4 +++ .../Images/excludeindexpath.png | Bin 0 -> 32667 bytes Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 8 +++--- .../Search/Constants.cs | 1 + .../Search/SearchManager.cs | 4 +-- .../Flow.Launcher.Plugin.Explorer/Settings.cs | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/excludeindexpath.png diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index ab9c8f10f..964a8897c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -7,6 +7,7 @@ using System.Windows; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Plugin.Explorer.Search; +using System.Windows.Media; namespace Flow.Launcher.Plugin.Explorer { @@ -18,9 +19,10 @@ namespace Flow.Launcher.Plugin.Explorer if (selectedResult.ContextData is SearchResult record) { if (record.Type == ResultType.File) - { contextMenus.Add(CreateOpenWithEditorResult(record)); - } + + if (record.Type == ResultType.Folder && record.WindowsIndexed) + contextMenus.Add(CreateAddToIndexSearchExclusionListResult(record)); contextMenus.Add(CreateOpenContainingFolderResult(record)); @@ -154,7 +156,6 @@ namespace Flow.Launcher.Plugin.Explorer }; } - private Result CreateOpenWithEditorResult(SearchResult record) { string editorPath = "notepad.exe"; // TODO add the ability to create a custom editor @@ -182,6 +183,22 @@ namespace Flow.Launcher.Plugin.Explorer }; } + private Result CreateAddToIndexSearchExclusionListResult(SearchResult record) + { + return new Result + { + Title = "Exclude path from index search", + SubTitle = record.FullPath, + Action = _ => + { + Main.Settings.IndexSearchExcludedDirectories.Add(record.FullPath); + + return false; + }, + IcoPath = Constants.ExcludeFromIndexImagePath + }; + } + public void LogException(string message, Exception e) { Log.Exception($"|Flow.Launcher.Plugin.Folder.ContextMenu|{message}", e); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 3f3206567..65eed12d6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -31,6 +31,10 @@ PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Images/excludeindexpath.png b/Plugins/Flow.Launcher.Plugin.Explorer/Images/excludeindexpath.png new file mode 100644 index 0000000000000000000000000000000000000000..8578ac7fb61f2167c5194494ff8aa4a618d45dbe GIT binary patch literal 32667 zcmeGDWmJ{j_XP}JhejHuK|)GWKnanM5Dq2MB_Q2MBY7|YX$~cgNOyNBC9QOWq_lu^ zz596o{^R}je1FDxbPR=kt-bczbIm#Tz7G0PRYi{A7WFL%f(YR9GHMWn0lvh5aInB% z{&bx0z@H{?nWq|_)7xJIz!-$cxXP! z3spxH9&bK9%Jfoe&cCZ!>JAbtmWrfzeBI#kn{2GqZY+6>SQe!#mewS`kp^YXF-9(wPL*bIcS*l;WCNHP`E z{Z7fLez-BzyvfZyd4Gd$Tx9()U{B)p@wDyDINkBok5x|>(T?0<;sy5B`_Dz(!>;VN zyA%kQMn=OG)8`@%bmNDrd}>w&c+%a@`e{IoIx1VW-R0J#yb97Ov#X~{9If?(PVT+s z-rH*mg#Cw;1E!6A)$9{y8`CKUC7SgX1Ypi(y1-I!!d~!tF0CW&g`H5x$uy&T4y||B zZvIMJq1=L)sdw)qu89hR5oi0pM?4b^FbFd43J+@S6gP}I_mrFI)*dF7&_7qczE&VC z(K|bi+~JvMDGI0{zcdEvjYHyYZX12yPkPv~zUwRPNAK_aW1_vPwJ5qmt2gPsZFeKe zQaEB*_p984g|Zo2Qk?B*8Z(j7**UG8T{Usln&UdUwx~imOMPlDmxxR4gM{rj%35oY z?=shNn@wkWskv;+i+VsBL^p%UOVN)ReyY&Z#VstqK*|M=a#fPG~!~#Sm6+^-=TK))#G5)BrFMZ!}vo*=bkp1f@61^m zRLp3Ij>qtu{ckmq&vV87Vw`97dVVPu6zlhisqMx&4|xWGYE+vK<~HmjJluUR8CECN zdwwY-)f~rq=glgvrrCh!;|3iKos5E6wEEMuph+Iu&IqJMc4NYBG54zB;RI+$Tfx+m zkKJw}^C`|L8?~3i1+|vj{mw-3-sRw-jtZOd(*2haMA@`6UB{Zv#K^T~Uw~S(?(R>3 z*<#sEdTS(+yNF+c^Q>l1fZXsq5AAyGKCz1eyk$6Q5A7mOa!)?*EH~fpF73Wn@A>*{ z__Ld3!t%B=Z-O_94eWwDAW53}(2&1PVqVJb{S1g(DYBDpe_OD7LOr56j`jhwvYfX% zz0r*DN6SYLB$+xa>*Tt*On1M#-V~ctL`gnrkT4=u|2Vdy_958bw4#WLyswYIYF!TU z-Mg%*&7^FxwfCSh9c4ww{k>)1`*I~K2nK7yP3OPnd;8qrh%5U>e(niRUY@z%O;AmX z!#rRv6x*CPN5$&^XtNqU__4cIMEvn_w*r6EM_gR(xBz4ncD1Kx%b~SAAM}H( z4;PzS#y1xN{O&&OXaL*~-zhT7NXTm6Xet+q7b^x2c57eR(#G_r|5&T5L;iY+jQPrYCc(l9slAfN|+Y&%OV!V7T+|uXd)$8<&nZKSJz zg1gB4LJEl-Ai!(k-Um}h`)RSuNQFQH_^`AGT&Sb*iUG7zj@RU+P{;lI-9{W#HZ1VS z8u9DdHBbGiK41@?>@On{B6kW*g5#L!+|7B7woQTB5ZHRcylIK&!iDi)HIY?M;+mVc z_W>4%_9G?2CI4XZCVN-FY|YZpynSRo
    HK(eJ4q27J0!Jndl)M^o1T`id>0u5nn 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�z=R%N0lXV3%mb$l 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 YF!4ZnUmtr8T#2kCuP#^o)co!L0ef9{uK)l5 literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 8bd6812d2..18691c579 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.ViewModels; using Flow.Launcher.Plugin.Explorer.Views; @@ -12,7 +12,7 @@ namespace Flow.Launcher.Plugin.Explorer { internal static PluginInitContext Context { get; set; } - private Settings _settings; + internal static Settings Settings; private SettingsViewModel _viewModel; @@ -27,7 +27,7 @@ namespace Flow.Launcher.Plugin.Explorer { Context = context; _viewModel = new SettingsViewModel(); - _settings = _viewModel.Settings; + Settings = _viewModel.Settings; _contextMenu = new ContextMenu(); } @@ -43,7 +43,7 @@ namespace Flow.Launcher.Plugin.Explorer if (string.IsNullOrEmpty(query.Search)) return results; - return new SearchManager(_settings, Context).Search(query); + return new SearchManager(Settings, Context).Search(query); } public void Save() diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs index a299f24c3..2f6a6f2ea 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -11,6 +11,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal const string DeleteFileFolderImagePath = "Images\\deletefilefolder.png"; internal const string CopyImagePath = "Images\\copy.png"; internal const string IndexImagePath = "Images\\index.png"; + internal const string ExcludeFromIndexImagePath = "Images\\excludeindexpath.png"; internal const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory"; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 33d255ae0..1ffc9ee51 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -112,8 +112,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!_settings.UseWindowsIndexForDirectorySearch) return false; - if (_settings.WindowsIndexExcludedDirectories - .Any(x => x.Path == FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) + if (_settings.IndexSearchExcludedDirectories + .Any(x => x == FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) return false; return _indexSearch.PathIsIndexed(locationPath); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index abe452481..53011bf18 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -17,6 +17,6 @@ namespace Flow.Launcher.Plugin.Explorer public bool UseWindowsIndexForDirectorySearch { get; set; } = true; [JsonProperty] - public List WindowsIndexExcludedDirectories { get; set; } = new List(); + public List IndexSearchExcludedDirectories { get; set; } = new List(); } } From bbca3cf202ed75543119f7408d463fa47041f0f3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Jun 2020 21:08:41 +1000 Subject: [PATCH 107/142] context menu- update text display and add check if already exists --- Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 964a8897c..7a24d58fb 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -8,6 +8,7 @@ using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Plugin.Explorer.Search; using System.Windows.Media; +using System.Linq; namespace Flow.Launcher.Plugin.Explorer { @@ -27,7 +28,7 @@ namespace Flow.Launcher.Plugin.Explorer contextMenus.Add(CreateOpenContainingFolderResult(record)); if (record.ShowIndexState) - contextMenus.Add(new Result {Title = "Indexed: " + (record.WindowsIndexed ? "Yes" : "No"), + contextMenus.Add(new Result {Title = "From index search: " + (record.WindowsIndexed ? "Yes" : "No"), SubTitle = "Location: " + record.FullPath, Score = 501, IcoPath = Constants.IndexImagePath}); @@ -188,10 +189,11 @@ namespace Flow.Launcher.Plugin.Explorer return new Result { Title = "Exclude path from index search", - SubTitle = record.FullPath, + SubTitle = "Path: " + record.FullPath, Action = _ => { - Main.Settings.IndexSearchExcludedDirectories.Add(record.FullPath); + if(!Main.Settings.IndexSearchExcludedDirectories.Any(x => x == record.FullPath)) + Main.Settings.IndexSearchExcludedDirectories.Add(record.FullPath); return false; }, From f2fffcb67531edbe04c2b5eb2712d52438027a10 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Jun 2020 21:11:16 +1000 Subject: [PATCH 108/142] rename method for better description --- Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 7a24d58fb..e3617b037 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -192,8 +192,8 @@ namespace Flow.Launcher.Plugin.Explorer SubTitle = "Path: " + record.FullPath, Action = _ => { - if(!Main.Settings.IndexSearchExcludedDirectories.Any(x => x == record.FullPath)) - Main.Settings.IndexSearchExcludedDirectories.Add(record.FullPath); + if(!Main.Settings.IndexSearchExcludedPaths.Any(x => x == record.FullPath)) + Main.Settings.IndexSearchExcludedPaths.Add(record.FullPath); return false; }, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 1ffc9ee51..ce94c67c6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -112,7 +112,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!_settings.UseWindowsIndexForDirectorySearch) return false; - if (_settings.IndexSearchExcludedDirectories + if (_settings.IndexSearchExcludedPaths .Any(x => x == FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) return false; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 53011bf18..37f687d86 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -17,6 +17,6 @@ namespace Flow.Launcher.Plugin.Explorer public bool UseWindowsIndexForDirectorySearch { get; set; } = true; [JsonProperty] - public List IndexSearchExcludedDirectories { get; set; } = new List(); + public List IndexSearchExcludedPaths { get; set; } = new List(); } } From 2c15f1ce70a0c74a7409248ac84969c4afe35f0f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Jun 2020 21:14:03 +1000 Subject: [PATCH 109/142] rename method for better description --- Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index e3617b037..6aba9fcb5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -192,8 +192,8 @@ namespace Flow.Launcher.Plugin.Explorer SubTitle = "Path: " + record.FullPath, Action = _ => { - if(!Main.Settings.IndexSearchExcludedPaths.Any(x => x == record.FullPath)) - Main.Settings.IndexSearchExcludedPaths.Add(record.FullPath); + if(!Main.Settings.IndexSearchExcludedDirectoryPaths.Any(x => x == record.FullPath)) + Main.Settings.IndexSearchExcludedDirectoryPaths.Add(record.FullPath); return false; }, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index ce94c67c6..ad6cdd9dc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -112,7 +112,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!_settings.UseWindowsIndexForDirectorySearch) return false; - if (_settings.IndexSearchExcludedPaths + if (_settings.IndexSearchExcludedDirectoryPaths .Any(x => x == FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) return false; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 37f687d86..ffa7fadd1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -17,6 +17,6 @@ namespace Flow.Launcher.Plugin.Explorer public bool UseWindowsIndexForDirectorySearch { get; set; } = true; [JsonProperty] - public List IndexSearchExcludedPaths { get; set; } = new List(); + public List IndexSearchExcludedDirectoryPaths { get; set; } = new List(); } } From 8f99d632cb022af8e7916abe0a3ad8810e3cd401 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Tue, 2 Jun 2020 17:43:11 +0300 Subject: [PATCH 110/142] update how theme files are copied to output folder Moved the copying of themes from the build script to the project file. The recursive copy-item call in the post-build script was causing the 2 files inside `Themes/ThemeBuilder` to also be copied, which resulted in the `Tempalte.xaml` being loaded at runtime as a theme. Closes #38 --- Flow.Launcher/Flow.Launcher.csproj | 8 ++++++++ Scripts/post_build.ps1 | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 970bc8e37..6fe1b817d 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -62,6 +62,14 @@ + + + MSBuild:Compile + Designer + PreserveNewest + + + diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 624f4f502..648b50b15 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -35,7 +35,6 @@ function Copy-Resources ($path, $config) { $project = "$path\Flow.Launcher" $output = "$path\Output" $target = "$output\$config" - Copy-Item -Recurse -Force $project\Themes\* $target\Themes\ Copy-Item -Recurse -Force $project\Images\* $target\Images\ Copy-Item -Recurse -Force $path\Plugins\HelloWorldPython $target\Plugins\HelloWorldPython Copy-Item -Recurse -Force $path\JsonRPC $target\JsonRPC From 06877ca06b8a68d00d3709075751d0d31a165b52 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 3 Jun 2020 20:15:21 +1000 Subject: [PATCH 111/142] change to exclude sub-directories too from index search --- Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 6aba9fcb5..38bf63f89 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -192,8 +192,8 @@ namespace Flow.Launcher.Plugin.Explorer SubTitle = "Path: " + record.FullPath, Action = _ => { - if(!Main.Settings.IndexSearchExcludedDirectoryPaths.Any(x => x == record.FullPath)) - Main.Settings.IndexSearchExcludedDirectoryPaths.Add(record.FullPath); + if(!Main.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x == record.FullPath)) + Main.Settings.IndexSearchExcludedSubdirectoryPaths.Add(record.FullPath); return false; }, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index ad6cdd9dc..65a2f4ac9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -112,8 +112,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!_settings.UseWindowsIndexForDirectorySearch) return false; - if (_settings.IndexSearchExcludedDirectoryPaths - .Any(x => x == FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) + if (_settings.IndexSearchExcludedSubdirectoryPaths + .Any(x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).StartsWith(x))) return false; return _indexSearch.PathIsIndexed(locationPath); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index ffa7fadd1..6b442384d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -17,6 +17,6 @@ namespace Flow.Launcher.Plugin.Explorer public bool UseWindowsIndexForDirectorySearch { get; set; } = true; [JsonProperty] - public List IndexSearchExcludedDirectoryPaths { get; set; } = new List(); + public List IndexSearchExcludedSubdirectoryPaths { get; set; } = new List(); } } From b90664d7601b2bc055fe23c98e699845a358500e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 3 Jun 2020 22:00:18 +1000 Subject: [PATCH 112/142] add exclude index search confirmation and persist settings to storage --- .../Flow.Launcher.Plugin.Explorer/ContextMenu.cs | 16 ++++++++++++++-- .../Search/Constants.cs | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 38bf63f89..e36b09eb7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -7,8 +7,8 @@ using System.Windows; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Plugin.Explorer.Search; -using System.Windows.Media; using System.Linq; +using System.Reflection; namespace Flow.Launcher.Plugin.Explorer { @@ -188,13 +188,25 @@ namespace Flow.Launcher.Plugin.Explorer { return new Result { - Title = "Exclude path from index search", + Title = "Exclude current and sub-directories from index search", SubTitle = "Path: " + record.FullPath, Action = _ => { if(!Main.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x == record.FullPath)) Main.Settings.IndexSearchExcludedSubdirectoryPaths.Add(record.FullPath); + var pluginDirectory = Directory.GetParent(Assembly.GetExecutingAssembly().Location.ToString()); + + var iconPath = pluginDirectory + "\\" + Constants.ExplorerIconImagePath; + + Task.Run(() => + { + Main.Context.API.ShowMsg("Excluded from Index Search", "Path: " + record.FullPath, iconPath); + + // so the new path can be persisted to storage and not wait till next ViewModel save. + Main.Context.API.SaveAppAllSettings(); + }); + return false; }, IcoPath = Constants.ExcludeFromIndexImagePath diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs index 2f6a6f2ea..c033e3165 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -12,6 +12,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal const string CopyImagePath = "Images\\copy.png"; internal const string IndexImagePath = "Images\\index.png"; internal const string ExcludeFromIndexImagePath = "Images\\excludeindexpath.png"; + internal const string ExplorerIconImagePath = "Images\\explorer.png"; internal const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory"; From e34bcbd1ba311f347c3490828d374fbe5f6901e0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 4 Jun 2020 19:06:03 +1000 Subject: [PATCH 113/142] add prompt press ctrl to open folder directly to folder results --- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 2 +- .../Search/WindowsIndex/IndexSearch.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index f70c4d125..0d7f726b5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -66,7 +66,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo if (fileSystemInfo is System.IO.DirectoryInfo) { - folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, fileSystemInfo.FullName, query, true, false)); + folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, Constants.DefaultFolderSubtitleString, fileSystemInfo.FullName, query, true, false)); } else { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index c2513820b..d224c9734 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -71,7 +71,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex private Result CreateResult(string filename, string path, string fileType, Query query) { if (fileType == "Directory") - return ResultManager.CreateFolderResult(filename, path, path, query, true, true); + return ResultManager.CreateFolderResult(filename, Constants.DefaultFolderSubtitleString, path, query, true, true); else { return ResultManager.CreateFileResult(path, query, true, true); From 0ee0fb8d2bf717abfbc1d30fa843c054c3040c98 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 6 Jun 2020 22:13:22 +1000 Subject: [PATCH 114/142] add Explorer plugin's settings page --- .../SharedCommands/FilesFolders.cs | 2 +- .../ContextMenu.cs | 5 +- .../Flow.Launcher.Plugin.Explorer.csproj | 1 + .../Search/FolderLinks/FolderLink.cs | 29 +++ .../QuickFolderAccess.cs | 2 +- .../Search/QuickFolderLinks/FolderLink.cs | 18 -- .../Search/SearchManager.cs | 6 +- .../Flow.Launcher.Plugin.Explorer/Settings.cs | 9 +- .../Views/ExplorerSettings.xaml | 45 +++- .../Views/ExplorerSettings.xaml.cs | 231 +++++++++++++++++- 10 files changed, 305 insertions(+), 43 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/FolderLink.cs rename Plugins/Flow.Launcher.Plugin.Explorer/Search/{QuickFolderLinks => FolderLinks}/QuickFolderAccess.cs (92%) delete mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/FolderLink.cs diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 32f6507fa..ce067e8a0 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -187,7 +187,7 @@ namespace Flow.Launcher.Plugin.SharedCommands } /// - /// Returns the previous level directory if path incomplete. + /// Returns the previous level directory if path incomplete (does not end with '\'). /// Does not check if previous level directory exists. /// Returns passed in string if is complete path /// diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index e36b09eb7..417161300 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -7,6 +7,7 @@ using System.Windows; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using System.Linq; using System.Reflection; @@ -192,8 +193,8 @@ namespace Flow.Launcher.Plugin.Explorer SubTitle = "Path: " + record.FullPath, Action = _ => { - if(!Main.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x == record.FullPath)) - Main.Settings.IndexSearchExcludedSubdirectoryPaths.Add(record.FullPath); + if(!Main.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == record.FullPath)) + Main.Settings.IndexSearchExcludedSubdirectoryPaths.Add(new FolderLink { Path = record.FullPath }); var pluginDirectory = Directory.GetParent(Assembly.GetExecutingAssembly().Location.ToString()); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 65eed12d6..f45a70c52 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -4,6 +4,7 @@ Library netcoreapp3.1 true + true false diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/FolderLink.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/FolderLink.cs new file mode 100644 index 000000000..379b5848f --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/FolderLink.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; +using System; +using System.Linq; + +namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks +{ + [JsonObject(MemberSerialization.OptIn)] + public class FolderLink + { + [JsonProperty] + public string Path { get; set; } + + public string Nickname + { + get + { + var path = Path.EndsWith(Constants.DirectorySeperator) ? Path[0..^1] : Path; + + if (path.EndsWith(':')) + return path[0..^1] + " Drive"; + + return path.Split(new[] { System.IO.Path.DirectorySeparatorChar }, StringSplitOptions.None) + .Last() + + " (" + System.IO.Path.GetDirectoryName(Path) + ")"; + } + } + } + +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs similarity index 92% rename from Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs rename to Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs index 4bddf8168..1ffd8378b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/QuickFolderAccess.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Text; using System.Windows; -namespace Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks +namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks { public class QuickFolderAccess { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/FolderLink.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/FolderLink.cs deleted file mode 100644 index 33c6d37dc..000000000 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickFolderLinks/FolderLink.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Linq; - -namespace Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks -{ - [JsonObject(MemberSerialization.OptIn)] - public class FolderLink - { - [JsonProperty] - public string Path { get; set; } - - public string Nickname => - Path.Split(new[] { System.IO.Path.DirectorySeparatorChar }, StringSplitOptions.None) - .Last() - + " (" + System.IO.Path.GetDirectoryName(Path) + ")"; - } -} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 65a2f4ac9..5d6550f74 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,5 +1,5 @@ using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; -using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; +using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; using System; @@ -28,7 +28,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { var querySearch = query.Search; - var quickFolderLinks = quickFolderAccess.FolderList(query, _settings.FolderLinks); + var quickFolderLinks = quickFolderAccess.FolderList(query, _settings.QuickFolderAccessLinks); if (quickFolderLinks.Count > 0) return quickFolderLinks; @@ -113,7 +113,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return false; if (_settings.IndexSearchExcludedSubdirectoryPaths - .Any(x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).StartsWith(x))) + .Any(x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).StartsWith(x.Path))) return false; return _indexSearch.PathIsIndexed(locationPath); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 6b442384d..d4bfdbbfc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,5 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; -using Flow.Launcher.Plugin.Explorer.Search.QuickFolderLinks; +using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using Newtonsoft.Json; using System.Collections.Generic; @@ -11,12 +10,12 @@ namespace Flow.Launcher.Plugin.Explorer public int MaxResult { get; set; } = 100; [JsonProperty] - public List FolderLinks { get; set; } = new List(); + public List QuickFolderAccessLinks { get; set; } = new List(); [JsonProperty] public bool UseWindowsIndexForDirectorySearch { get; set; } = true; [JsonProperty] - public List IndexSearchExcludedSubdirectoryPaths { get; set; } = new List(); + public List IndexSearchExcludedSubdirectoryPaths { get; set; } = new List(); } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml index 93defd985..9e04c8b8f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml @@ -6,7 +6,48 @@ xmlns:viewModels="clr-namespace:Flow.Launcher.Plugin.Explorer.ViewModels" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - - + + + + + + + + + + + + + + + + + + + + + + + + +
public string QueryForTopLevelDirectorySearch(string path) { - string query = "SELECT TOP " + _settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; + string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; if (path.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > path.LastIndexOf(Constants.DirectorySeperator)) return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index 298476abd..c0bb60c40 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -1,4 +1,5 @@ using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using System; using System.Collections.Generic; using System.Text; @@ -7,19 +8,26 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels { public class SettingsViewModel { - private readonly PluginJsonStorage _storage; + private readonly PluginJsonStorage storage; - public Settings Settings { get; set; } + internal Settings Settings { get; set; } - public SettingsViewModel() + internal PluginInitContext Context { get; set; } + + public SettingsViewModel(PluginInitContext context) { - _storage = new PluginJsonStorage(); - Settings = _storage.Load(); + Context = context; + storage = new PluginJsonStorage(); + Settings = storage.Load(); } public void Save() { - _storage.Save(); + storage.Save(); } + + internal void RemoveFolderLinkFromQuickFolders(FolderLink selectedRow) => Settings.QuickFolderAccessLinks.Remove(selectedRow); + + internal void RemoveFolderLinkFromExcludedIndexPaths(FolderLink selectedRow) => Settings.IndexSearchExcludedSubdirectoryPaths.Remove(selectedRow); } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs index 104bef336..f36c3442e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs @@ -1,4 +1,5 @@ using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; +using Flow.Launcher.Plugin.Explorer.ViewModels; using System; using System.Collections.Generic; using System.ComponentModel; @@ -18,13 +19,16 @@ namespace Flow.Launcher.Plugin.Explorer.Views ///
public partial class ExplorerSettings { - public ExplorerSettings() + private readonly SettingsViewModel viewModel; + public ExplorerSettings(SettingsViewModel viewModel) { InitializeComponent(); - lbxFolderLinks.ItemsSource = Main.Settings.QuickFolderAccessLinks; + this.viewModel = viewModel; - lbxExcludedPaths.ItemsSource = Main.Settings.IndexSearchExcludedSubdirectoryPaths; + lbxFolderLinks.ItemsSource = this.viewModel.Settings.QuickFolderAccessLinks; + + lbxExcludedPaths.ItemsSource = this.viewModel.Settings.IndexSearchExcludedSubdirectoryPaths; RefreshView(); } @@ -106,22 +110,22 @@ namespace Flow.Launcher.Plugin.Explorer.Views if (selectedRow != null) { - string msg = string.Format(Main.Context.API.GetTranslation("flowlauncher_plugin_folder_delete_folder_link"), selectedRow.Path); + string msg = string.Format(viewModel.Context.API.GetTranslation("flowlauncher_plugin_folder_delete_folder_link"), selectedRow.Path); if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { if (expFolderLinks.IsExpanded) - Main.Settings.QuickFolderAccessLinks.Remove(selectedRow); + viewModel.RemoveFolderLinkFromQuickFolders(selectedRow); if (expExcludedPaths.IsExpanded) - Main.Settings.IndexSearchExcludedSubdirectoryPaths.Remove(selectedRow); + viewModel.RemoveFolderLinkFromExcludedIndexPaths(selectedRow); RefreshView(); } } else { - string warning = Main.Context.API.GetTranslation("flowlauncher_plugin_folder_select_folder_link_warning"); + string warning = viewModel.Context.API.GetTranslation("flowlauncher_plugin_folder_select_folder_link_warning"); MessageBox.Show(warning); } } @@ -138,13 +142,13 @@ namespace Flow.Launcher.Plugin.Explorer.Views { if (expFolderLinks.IsExpanded) { - var link = Main.Settings.QuickFolderAccessLinks.First(x => x.Path == selectedRow.Path); + var link = viewModel.Settings.QuickFolderAccessLinks.First(x => x.Path == selectedRow.Path); link.Path = folderBrowserDialog.SelectedPath; } if (expExcludedPaths.IsExpanded) { - var link = Main.Settings.IndexSearchExcludedSubdirectoryPaths.First(x => x.Path == selectedRow.Path); + var link = viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.First(x => x.Path == selectedRow.Path); link.Path = folderBrowserDialog.SelectedPath; } } @@ -153,7 +157,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views } else { - string warning = Main.Context.API.GetTranslation("flowlauncher_plugin_folder_select_folder_link_warning"); + string warning = viewModel.Context.API.GetTranslation("flowlauncher_plugin_folder_select_folder_link_warning"); MessageBox.Show(warning); } } @@ -180,8 +184,8 @@ namespace Flow.Launcher.Plugin.Explorer.Views if (files != null && files.Count() > 0) { - if (expFolderLinks.IsExpanded && Main.Settings.QuickFolderAccessLinks == null) - Main.Settings.QuickFolderAccessLinks = new List(); + if (expFolderLinks.IsExpanded && viewModel.Settings.QuickFolderAccessLinks == null) + viewModel.Settings.QuickFolderAccessLinks = new List(); foreach (string s in files) { @@ -203,21 +207,21 @@ namespace Flow.Launcher.Plugin.Explorer.Views private void AddFolderLink(FolderLink newFolderLink) { if (expFolderLinks.IsExpanded - && !Main.Settings.QuickFolderAccessLinks.Any(x => x.Path == newFolderLink.Path)) + && !viewModel.Settings.QuickFolderAccessLinks.Any(x => x.Path == newFolderLink.Path)) { - if (Main.Settings.QuickFolderAccessLinks == null) - Main.Settings.QuickFolderAccessLinks = new List(); + if (viewModel.Settings.QuickFolderAccessLinks == null) + viewModel.Settings.QuickFolderAccessLinks = new List(); - Main.Settings.QuickFolderAccessLinks.Add(newFolderLink); + viewModel.Settings.QuickFolderAccessLinks.Add(newFolderLink); } if (expExcludedPaths.IsExpanded - && !Main.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == newFolderLink.Path)) + && !viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == newFolderLink.Path)) { - if (Main.Settings.IndexSearchExcludedSubdirectoryPaths == null) - Main.Settings.IndexSearchExcludedSubdirectoryPaths = new List(); + if (viewModel.Settings.IndexSearchExcludedSubdirectoryPaths == null) + viewModel.Settings.IndexSearchExcludedSubdirectoryPaths = new List(); - Main.Settings.IndexSearchExcludedSubdirectoryPaths.Add(newFolderLink); + viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.Add(newFolderLink); } } From 6befd6cbb14b8c9ea2a92711a379b1afef952b1b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 8 Jun 2020 14:47:57 +1000 Subject: [PATCH 116/142] small fixes - ignore upper case when determine index search - add open with different user image - add subtitle text for context menu containing folder --- .../ContextMenu.cs | 3 ++- .../Flow.Launcher.Plugin.Explorer.csproj | 6 +++++- .../Images/user.png | Bin 0 -> 65650 bytes .../Search/Constants.cs | 1 + .../Search/SearchManager.cs | 5 +++-- 5 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/user.png diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 26ee16975..49318cc86 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -136,7 +136,7 @@ namespace Flow.Launcher.Plugin.Explorer return true; }, - IcoPath = "Images/user.png" + IcoPath = Constants.DifferentUserIconImagePath }); } @@ -148,6 +148,7 @@ namespace Flow.Launcher.Plugin.Explorer return new Result { Title = "Open containing folder", + SubTitle = "Opens the location that contains the file or folder", Action = _ => { try diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index f45a70c52..0cbe94d86 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -51,7 +51,11 @@ PreserveNewest - + + + + PreserveNewest + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Images/user.png b/Plugins/Flow.Launcher.Plugin.Explorer/Images/user.png new file mode 100644 index 0000000000000000000000000000000000000000..2d45c1ee919304a973641b387934dbd517979271 GIT binary patch literal 65650 zcmV*nKuEudP)KoEefB#uyts#u%fQWvShg+Wqc(&#Brg ze^l+-YrpDLoqKNg?e4Q|^{uMkUTgnq*WPQdy=&J#goP|*R{5P100{Wb>wy2|3E)4y z1vh`_6rhkZBgguMoQv@ggV8b+Eu$DOV=`W$e6T|Ka1}(WD9U9$K6gF1+y$aFkXi$1 z6+~-b9)a}=z(W8dFmiwu00jVC%?_NSy`II+#xaI1Sb(!TKarc0vtz zJ`a_h2lzA>jYd()+z44*}#L%E7Gah&3T1hHbkXb+{1)p_i~MjcO2SFc$y{C^ZJD z^I*LW%1+Z@=>*F0aX>vygQdUYvV4?TKZ(KUB=qF`WU#cZM_NOT^>c69*P7Ep7P4@> z3#sHco~gXkHA5VHE`y#R=kbLhseBcLJt)ilAUed83ojy-A7ot|gf91h)CgJ+!8)%D zF&I!yyotJ!MVuf5AkQN@Seu?lf4WTugF12H8X#*!+elhfPFd@9sO&gp`ID@R$7#6Z zGtm4vvg~OvjlXfhT`cPHE?EJMejefaLNl=INpW)+DYsxCm89$kc!DNsca3C z@c{sbpeJ7o&9_0R!(2}G16YABR?P69!HU>8r0R+}VXiz^=pY$j0#SVofIv2YWd;e4 zC)_1iV7~LM@-x;v+cR?UbK?4#Y-$al;-vDk0M1f2cp6$i3Z=dPmH#ckk0W1ticy@@ z+3@@Z@%TiZ18=>i?Wru}3YUfBy-H=@uRN>Nq!<$AYplzI#C$uzUk@&CgVwjRE_VaG zQcV@Gs+?4}?~#sHlql|2WgJ`c*i0HXiMM4u;>J;}N_sRx7cGw07~{Nye3daMh%Ol0AB zuOeCc&11Os>@Xj4zJ~FIBhdOTl#`c&a68IkFTg9z

PbOc!t+-8s$(Xa?|2G$~zC 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≠V82n0$h7|Q}MW-`-nIZ*@(Hi-qZj`A#@9Yv_z?V!7ZquB6y>JBJYNexgamKza7lViSHYss4l_O^8GtXiC zrN4$gcLF!AU4vKN^+H^K&D2#gbC*l;K}Aa1xa``I;eo?Dal^7|o{RGzMXtdXeKb8u zjN|sCUHNR;OYdWCdJAk*7?_7d>L{HmS5x&R>Uj5l@L8oN8+SmLM+i`j$;Nq!T2V{o z_u{SD_W0DCqv`Se-5gwzfxb5ETwJ?qVbY6pInS;@NWj+@($bWwgNJJC{+QhenS8v@EFtA~r=U z3to)d{I;G8dT^08(_E-C7Y2hBm&F|zuOHd}$aB#~6TetahS2&hXgzetq}F+v-O1$r z%4Jt}7FYBHZgDt0M{}>9PErqk-toUt%xhoN{1ZwnkIq7IE~yTn8On;v5sVASO&fN2 z)wyieu3m&-bB&?TK97wreFnUK1^{r)jwQVE#V^EYI9SZ>dg4rL0N625c;N6dmUFj; z2EhvXwb#XhF_OUKv!*ti>|>%UU=h63`B}{FkznpYb|D-IxU5YXY`Ie@P21ZP7a3Og zT)6q44wnp{x^RIMcEq4;w9Bj zx10O2y|B40*Xg@W-8xLy?YMEUldyIEa9Wd(ROL=sADM?V5H z1YbDaqAT4_szy3H7Dvf-sS}dCDXKVpC!+Eaq;CF_U^!o_6=n)RvU>NMs`6M>P8*rlFvJ-$sg_s5NGS8rA>m17HS*1u50aW0hN z(%fJ5oa0>N(t-|LhyX4X%Hm~|XS?vnUxY7#AZ9=Bj=z4(_zVSdx>v|XT%hD{{U=*j2BjPmp` zjGz28^qFUyetvm4z$;($LR`19+}5Akg4J2=PwRq$046PxuYnnVk0(xP{g$!$RAsU=~?KJiO}c9g{)qz4m1P~L`eve$64Qvf!^ zcITm->>T$haXJ0a6gXQ4Z1{7ahfvQuk4dt5a)`;Yi&d_vHX|6d=%t>sA1Fy>ztiet z*xfTj*wh;3sb?yud+w_+jg}d>>H1Z?__hNWN3*<8GKa zmG3P4I_O~VWX1;4C;IJ*^>OT*k>*9(sb?^L;xD1ko~ZNSrkw9QybrhB81~j(B{f{{ zbL@XP)+Nu_2Nqk!C5((22iFETvS-*dNA~|Cdo1xFExYL<(jbH<7%FM60$b z^~mVeCzUN~{FA>5Uq-A=1ZeEQomZ`c&P{Vcgp0}F=VI?av(Ux7L<+Fpm8fRk8C#;RywMQm_m{_?nbI^q(w4;bIcLUix{Jrb-;F%5#OE6 zjB|o^EGu@ThP$fO~%Pycy5{efBJ@OX6}2sow<#A~OfHoVJxtkX4L4j1DURd4*-&fY`})bdFu1qaeJxi%?+saY_% zHXVe*G*dB}f>lTElCkG($5ac0aoy@NUi88P(QO~Q%GD6@`oathGs27Z?ZB=bvg{~& z*vvgft^I-O?wZ%o_`Mz_Ykw`>2=}4tiU+JWqIJHXWYdNj^iyg=KvpqE_X9Q`ELio zwh}TB>`vAcS$)$~hj(%(v1)ixDcN3{Ehg?I&`V%4?}kexNQ&)T)=Sv?yCqm8@!T)~ zTzwp|@#LqQ)eBY#RX0f3zw4T-W(Ts(NVusEK#Fj~&J25YR*sj&zfD?mg{2#^m@{YU z1q0Rz_+uM)DOPipY^PkcOKKc9k-gqT2F{BpfwQ<)r!n^Ieg?iCIeMGrLgN19ZefOt z(Pc7I3f8834=x75C58cReqFc#P^`<{#5hP=JD%5Zyn3=eV1j2TMM4KRcf^HJ$Ut(Pp;=5Sl`a2g^zXCCHuBGCpcG1e$f z9mB?xp8~H}PB$!zs>eOF_d4uewNq2GeCTjBPBl~sfnCcP4qlr>Au+Xoc&Sh3Em0D!C zFp(d8J%(LQ7AymjcjMcW-GH}qStl+4z^kmwuhqqQXPZdJ@lrNeCDylAA5Ku+s6~V; zvSm|yB9GWFNT=5b(hB<=$9$VU=7=0(r7#LH3J%(BcS=qj5=9I)9Yte=B~hN@FHi2=gNldv0mdTq-?hw=lSRflWeH*1k>26JDt4a7p`{ zb}m5WOZ9D(WzOfrjpM!kqmP1gu^U9UR@ZHG%4`Bmn?WjqWQ5g%BA9gHO*-;0ZqZ}+ zJP;0f$dbLP+=$lAhBwucF;s=!yk@RTxURFKfx^wZS1}ySKEHdlNGeJmW`up$jz0bH_gLULm`(6dYtyD~QOGw!?%L=7Fz-6(w@=PMiG93wQ0kW6? zZk(Z<-*9>XvU=j!kK}?dp1NVC9GnrE*U5^0L!{$osaZ2`?r8aSSvE2_;;dq~KEHjN8d17)!nj05{Ve75no_F!YE zpz=dRv}zilX}HE+I+d*U9t`RHY+Cbjz%2rQ?n!F5N1@XqRPxgJ$8n6T-=Po#+~?#r zGCzOOZwjC0rX|PExi;greeSaJAcQ=8J$>#3CQpA3`us^ZyXq3;1rhDhOxUhIm`+ zPrSG38H#Njn#hAf%&#wV!CJV)68MA4a0jw@4t}L=BHa^DV&jYdAM}}L-3Cg^9)nO- z1Jhdo%YzInOIPO)Sp5H^ZKZkKVi-goDwofYZvYH^?s<%#`j6m^ zv;NJ5ZCi36!cvxDu!;4%SDJL0c8F+UkQ4IEjh>7&bsq56m&c-lWENg}(ITYH*l^m( zPdIhC+$s_`IxoWojXK72f-F~W>$o)OJuYz4PUqrxG4tXAW{}DbQ9fA4SF2tBsi z03=_NR#IZ`T;l8<2bk~3*@o*96CH}{oLlluVVjkCJMj1rY8Cv&P#&i`+YU+UiXl7^~)_Gr&b+KMU7uZ>5Q1WrJK+KhUyK*!aAEC(exbQWQ4Stbn2eR zN=CZ~%5YN>48Ia7x?!zp*!lz6Y? z?5<|ovuWqeb#srkO^MCQ1u>ic(l$orM-um7xisJ7HTPg`$Msm~_2#1W5R>(<$@M4) z>N(y_Wy=8egS9e+TYa0>oH!p)ROW1E<{M}8Y)nUDF}Qk7PtzENwBDSH3to&CcS@qy za^`W_!!U1Y^W>YrlCX<8OU8`&J)k3Qxi?qqLR>F3#%0;Ut($D-_s^u>JieOSDlW-6 zUm{FOjY6}XcWnX=8^C}dSAIDwZ>Bv$zCzNqIS~tL|+OT(zT&FcW6MELR zXl#uhC-i3F0?%exE>@3P^b%1`tg1OSXXK(w1wxfO zf&Iw!)ZQk|P+zE;b}TR!=}pSiE?mJf(3%i~>X&h{M=+5hZJLX;=`Qc`^h5|7=t)el zu~Qu(Pn(`*<3do#2m4VLy8%G$`_NP1jq__3h=pLCddm+#kCXH(=&G3@=f+ zOh;(=w(d>_PcMPVoAm!iu^!%Z{?T!_9OJ7$M05p4dG-V*U;Z3;<6OHv-`UFZ$XoTI zC~^ASI!e8{Zs%&4YDhWdQUj+h6qu9|zbOK-*!9LxpK3R~K`jhzSAs3oF^VN-0+CY!Mo*JWmuXP(96>A!(K|5ef0N&G7=IWN@&;9M8a9fb8s ziKm`FgK<$djwTj$&N^4>OW!%%t=uVXKJ&@IxL`bUw#2w?s(fl*emKz~IpqGeh7g*% zU_Q!;VuDi;ZIg`Z+u|QaW=j*b|*0w7F^8cCvKnZpJqD3;}|r3 zY0t+Rs0C}&OQiQQ7D6pp%4u;LV-0=gSxlb(JoJTA|376($ zF*Dc~5+oLaAVHBLMVS;STP(|#>?pFVSV|;I6f3e-wo6t1D^@C%ROF?~m2y=oj*`e# zDaCfFoO!-*jeneqDNHEe9R&-3mz0lt%to}3g*&NC(g zHU=ZSzBa_Dty+u)F=c$?DMdp*b??myxxnhNiKFzmVT&Qv?K7(vrM){W%1i?mIYVJm zEc(oaRxJLpBBd#?0063L9)q33ytC0Ms?o5=gPmijM)M|dedRqw0qC6Th}EU(Co=C) zk+NWu@v_iaco*K=L|mBR!%G>J8L^USd2(_Yhxj=?PRd$s8jPWwe1vGE*Jw!p4J|O&)z|Heve)KbEBS>p+d=CiSG_mK>blj*2zFRq$FjY` zVmIqW^;*?$j%}DY=o$FFH+fA1<3v=kR~iK zq35%FEOkxIvRE$-tcDkoXs#PTA`=28U3RK~y|YI;w!3DLNl~YIB(hMG!W>VchmamJ zHW8(lZFQk()lnJM<~0o7d>OoTqpnE4<5B%}n`hL}izk)T**kxA9ha_eyranQofz`R zLIRgIhPb#NU(K85Cdt--q)Mn2ZcSklv8jr+ zxAk0Cu{T(9csPU8x-x0VVM)q5m+pjE+|px*itZ|P7v>8p%0(3AQ4n>cC-&Prp!m&u zgxa-p|6`YcUPmypv^5LC!-b_T5OI<4_+p= z8N=&uVCT%!^{)XtRwpVzgpj9*Yrjty@KA^~>0d$=h7ntx@5J zDS=EYL!emOs$)`%w~Tqcn}Q`5@>pyXYjZLzG$R%Qh&ohuj#5!B7Z~o$^JusN<|6hM zBd5~H!gewt56YZQMxjk>2rhoytFU+nkRFF7l8IekK(OyO$hcYN)5*!$+-GHbU^9*b zFh-l#FnHsIaLSm9;6Gc0>vN5<@JVBuh<}-J*X{r8j4FKlwF_9=+!^cjPP=#Zdnrtk z_xO5kr^54>`WSM)&LF7|w!vnaor$0KA47*-I~j(dQw&Cn%$=QjvOR=7?9rm*$c}g1 z%`pCEV9^-rxH&BB^n!o@EsL-~=V4Ed1dewyvUB|mD`Bzj>Sxvz1yvyK;gzc7n6)(f&Wy|j}eSi5;yYSTdF3ZomBFnIk1 zaDNj~K}A+{f@uXeb;clRepUeZ_G=e#=E5~K3fkK+D_QUMmnx>gGR}jKry7_EuU#A9 z`HTJVN;sugd0sma5!PIft@J2kPwT~P%n*y|tc8r!bLJ>M{PV1vpzKkkmn^RA5{v%X zoh;R2XFr6BcIhXt2WloyCGW-Bl2+!2cQr2dk>k7s5d79iUQ{@-@ubEeAn#f-P3%|I6bl3U#i$kEIKfNK~>>T&u-%U#z+-u zWHWx=B#%ZR3Tz&Lal-VC7G-o@T_UBYA^0a|L3aWyu3qgW7P-j&z@+Fk+00-GQ8HYU z!D3|7V<|vbp-#D4P&v0kMLDlDf;T3vIO}HXhB9s2$)c|p$jkJ0GM5HxJ)3cvhG1wL z((6Zsg!DBkDoK05C~j^T=pUt99#XxGV$e%%HVzoM!bnJWdoAN8H3iB?qUc#t7)`ba zTZ0iE|FcuLdSfdaYuxX#eLn=d=E1nKHNqFavx$CoADP7S|I-iBK<(lTn4qe!_3Kyd zF--HNu2FJNX!Nw>wkcRbjoce7nsAaWV-X&!*-T} zJtLEzFx$!IiE^}Y1%vZ1)pNS8<1@du5l`{N-fDtV7S}{Kye0p}{DNJuMF5I|@bszk z_~v)sNb)9WPwOGxOrpLSQx+53a(};dehbfD7@){iBoij+QMvJ^!6fCc5!!(N$55wf zsZ+R6H$+B8MzKuU^65AkS(}WE-qctuE+cbJfLU+I<-uP^dh%a3?_s;D*P8(g01#-N zh?WafgT?yeiJYq^ov53~P+J^GWqsw>9T;a$^ZZ$07*naRJ6ffVDa}1Z0uC{!b=<27^upe>$KY{YA_ttlL#9{HXR0k z*{}_*ZhGPKSkf}ur3_Qbro`eYBd4eSA^O{jMVBlik0p8~vtp^!sCpO;mW!&td5Ei_ z`ej1`IH-mr)O6#d#A1`HEi$bq*KA}^GUR34EOVVsD+W?=V_B0!!O2C6C##zU*`&u9 zUVjt)v(JM4(T=jI+eQuEFQu6!i5R{5z_z9k!pgHN9qm24KO+YI-)BzaOW!(;{$QBN z=xlp!OSaS9URfR8|-oTf?yIp%X!9{Y7vkEW1QWZlgpSX>(0OW`D?BdrlR(h;$ zG6STB_l{(|o|*nrl;HG`=+%{}`4leL-zoHLB=skRi5; z5IvPoLUY>sZ&ntyFUlpD3DY%W_!k#ru}Lwi&FdImc(vZWpvaL_hG`;@wxrp2^IF#u zc5OCiH$1y|eG^}N`a8JNFxUJ27IGf-mCX^JIK7E0n-#(fi=36rXapIVovO34#_d+J z9^)1BGSBlNGhs*#fZI+4LwYVju$kfxzDGPVn-vQXx}F$|nvh^wLz4EF!@`~vy{_72 zVX<(SYEdnC7A#=ypc)-4K;^0~n7ZTAu3DbT>fd1sPwdBa!a2z&5UMJ2i0o}fG8OWa z!>K$rD|p7*%7YppaEhSqAfC4P(}bIeg#kSI-Az1kdJ8*ki)tQ>SDpyV#1)Sl zZ^H4bYFMn{kRO8QPvHWykCW(eeRfNt=XfO1+Z7gSz(T&(CtF?Hu!trj)$>@w`6qtk z!Hie{6rf_Y0MRN4{s43a{G4F@-i0bjRd~{#b?DKR4Iu!%kvcT^*c+&0>hyHxbv%yny_}yX=C!t{rSK5FQEXIS@j^j8i zN&aTVLa2!sSYaT0XsDzarT{_$V^1g3Ns5=PJYG&@A&=RuX39P?_qotmtvMr81)u88 zCX0JdpFCHn)~{f2;Z^YVx{~H9w)`)=xbQ9_vTX~VjOAVvJ$Zgl>Yw*~3w-(6Gx+^K zc>&w~;rIPEGN2~s?Lmd#e|`;LJy$P2W?#d&cN3T8gr069j<&X$h>^@X#~GP_WZkB* zdf|b&_BQtz3$^KGu+ZC#g@Fo$l>$Voq<`{9ne$h^a|nKBvYkw=7TUQU4KTNzETkqG zi+tz2jDt*Wo9dmHDI(V?n7FYldIsdnhAQ9ZQ_| zW-Ku2O^QWtdz%G|dmk$WnCJDw(A?ONS?vu4KT~_0q!NSI7n$~~^mei+vXO-hm}o}N z`n=v-Ba6?^m$I$wHe%7QGx}FQi_b(+ddj(K%3C{@cn|vC zW63p(c>7v5%?Yvi`P*5s)VhlWxLVd=?5Dx?q`AAS|D=<kHH_-k*O^d-CLRHQSw&&M+b$H6(M>ptAUNyE@ANci>TbujsV=` zJB53PkBI8i#fX&o5XVp4QxPfyl;y8Pe8Zvbu5+b$L6fBJC`?Fh`lAZ} z`isxt^H08n?f$^q)PE-p@1%1VCXK&67~v0Ix`E&Q_6CMk!^u}b&Oh-%I~D%g#Mi@B zYcuDJAkxuFx)ZKrxy~7q=g6Oybp(>`T-j6$Uov$lu{hF0MsK%Rv`x-n@nxrENx3x0 zTDRwAY|etkeIM5=0L+Id&LfIhEEB?ZGUcghnIvk==q+Pu5=p(!Zo*Dw0edi=8#;Jd zCO@29C>giNGPbmgxx#4UN=>?ht>{#>@B+n9x-1ZFsuAy0%hY{{q}yJcab5Z2zcyt3 zEFzr0zKMVF`%mJDXHM_x5S>ZRdzj|OF137?4=Bdz42BiHbZP_t^6S@eX{+*YFzou& z2J1gb8L~JJ!9hvuXV|7>sthO86t1}$OCSPP*TF_uATqm773tcV7|4p%wR+A7Te!%k zxf?8EA>O_QgmNK7<>F;(1aD-5Dz>L>HccPRf(5+-4D-tq3|5{YtLM?ZlynAiT#%;M zNMI|yq~7M^#WE8juP-a3R7S~#lC^MCn~y|b@aA<4FP^Ta7a+Z=(^SKJ>FvWbfZU5G zkR}}b5d_abbc9@EU9oQ_&!VakWmt9ahUA|=dl~=y-~9=`_TpLeCpa*@DSWT|*kylM z;cMUB#J~93HN0}geZ{9M&a}AHJ8OtA-SZ|a#?Oh`lyWug)D#x>BUJXpqeqAq3!C+fU1MSH6%3Ak^2e@<3t5Iig)3L> zjhezqs|96baWBRQ-msUk)?=&3Hf|C)Q-!!imPDFV*5D3vh0(?}3@)Cke}Y}eR{kZ0 zBvUU^kt4R(WGN+?^a$!NscNlp%tMcYlvt3~EA1%OQ2@2$$nA#i}n_bX!+-ZXPvB2qI*0B4pmnG}i7Xc;nLd~`y>=O}-5=t~@7};~e)%$GLnh?CYgV|O2$gYv0e)nv(-#&#M`?N^!&CdRR_cNu4w9jk$S_T$553n z*tuy~qU*6(qJLwtNZgsR01OlbfWn@DAEMTTRIAsGHfoscn_;cNO|oFKsT0?B;zIE@ zTRec2$nAA}FIVx%+r)~V+g}@KLeJoxb&Rfj7uC)Nbf@O^^e`X#1;X^MAOV3WSE8U3&UR>;MRa(~FE!R*-+cSb1^oB_`b+rAx6k-JzPVw0+rn4dFn^K- zvsu$?lMO}{zVhld{F5(T!V8y&>Qw~K!guO^1=zL|Ki8hA#MB}KDcj0zGyyY zke-1EY6k+0oHF-K#3?c;dHlT2j4KXi*SQ3%c|D;Gi=A>n-ZB|uw0;@GORs?kTbA?~ z09CFPR;Ggad?S1vqrVbj*9q#i(ok`98XRo7#^5U&2U|LBtcygh{<155muHST?5e78 zXlWk*^^boDKmEfG;g*BTD2nkX)4tOJ1E@w7-ne=L|K=NS;He~1b{f~CDjEcF_I1=!Vp*GcA1+f&%6tA?5btn zhRDF+?RAVUo(2Y6Dh2hvU>-M=lw5_5h>5)iW=esDWSvariPaEEykaI>){Q}!-&alw z?gDWr15Q;5z*c{V7tUP7^^I+uJiH%=_b;aPv7>v@oojhNwbo!&%PGE-J$u3{`{%e5`fLesvSw#!N{w3oLJ z3nJ}Fv0#s}0I2BXZ@x4Dp<^5<@t6!vWokh5D2lr1#^Kt(I9407S=sRMutj>IW#6hoTa%ge&D_n_=Ugp z0es{=w_$a8zIB@5oe(N!TwCA5A3u8iAZw_9<77Itrj_$pr^J{`akP0I)uq=^^*16>`06G>nI&J5>6BsO8bVJ9QoTI} z%-Kh>f~X3VjjqL=O+QH|ROZkJ1f2rFvHgqq(GT2-9p z8VduUZ94gnU%mn6Wl;=GHw^CM4!CJfLz8wMbg<}$%+7eO3|KwXGP$6PUM~GuXIhW9 zk>r`dTh}qX{N4ID(rjvhLxu1XLIc3@!Eia^4cB2mU<)}0fSEYKX5#tkp0m+@{@$o) zjLawjJbLd5{FM*C8$bN;UAXJ`0W8jS0nl5)YuO<4)0;a3oO=B-zWDU3`1gPE9G|U;dtp5S5tFZMw61*bCi|e7 zWm-Y)_{b3$0$Bu^`ZPOZ!oZdb`$!@zZ4$Jp8#;M5y6;eP(f7x2t?&SR^yg!%hEiemA= z#1$lI2a_mC@r{Y^>#1g`8L)6JQqmCrT=rJT+|Hv*bn+j+at_QV>S1YEOVPDd){gG!pyEViE(C3DA=2frh!>H4oF9{%&aeAh5U_8qi~CL<#HSv;7oU9pJ$U%e zW9W8DNY2y6O+wrL-8e{m`KvpE{&0k6PQ8K0zwt62f9hqteC{%CZ1;i1RrKz7KRT-? zvJQlUrpiTK>kcddFL5Z8Mda>946VWKAsP+H>kFrj6g?5ca z434>*t2K44fE3|X0Ald z;6^g-fqQ)N$n+eMEJ-~t6SlT8H+HL*_c=>A14&UJD2@)|}}r5H9G zXCkY=`RqsK zX}*U}QJ^T~YrJ5dnNcxgII6I*wSy}+Hu2h<*KzLRHN158B2K+=1*hJ)f{WKTu+<-c z0d%@0m>I?LVf1c&$dhhLZPZ#xq|O%waN9};+e5~i8cwUr?jX;3??#VozAJMP1M1 zYKEB>i+{A)0kB53yITOl@Bor&0vlp?J@ismrioTyMT!9)g$!k?eXhRE@>3|by9>#CJUQ}}j-zhH*cuG++NCv| zxx9uydj1UNx*g1SJ2L5zi{U~e(sU| zs2KR|Kf8{vo!i0o2&z@2_RK<3k`4zsr@Fa7=G8{DURN65aVL6^MW>9O^y>AKAOm2% z!8ASC*t^6+%~-sbn(Ud)V{z$fN3s=5H19eq7MG*74!~=G{jtlMoZ=4#T*XusSIsyi z#Ns=#=)NhgKZ_ym5i>S|o-HU-Cn?p@pi<8!j~}RC3UA-Q=;}FC>O5W2L?%6&D?oKL zhH_$DWs`Gp;}_>!j`%vdr{M5%oGo1ftaJ^Ym|cZnNZ-dr#5k}#kDvU3cj4!M2#xxgQAPve63Vg2KpmLf7Hi~tv)VZ-vqn=3IL@P z6#)5^lNlH>`^|HKJY`;5l92QX<-SAc-Tny5l_Q}d$0Ro0R@=X%9T(5+GoK0pw^Y_rX))N zAOU6wp#o`dMq$itCkviw@Oa~`X5Oai0|jH^mwT*vkDXkGnkU%P1wkQQ?z=D2v^ZXN z)}=_bJn{H_o6<(MPg%$BhUxsC`pzFgv`9$dz+e&itTIXsVofU+PQSSa!Ev3VR>D)Hv} z5SO>A#3`68ktyRFZ%AE}y-$fwHMH1M(|U;)7w}Qs7DX>gdv{nc7E5T2`U}q{EVYAV zi<$CRyiGlSOGDS4vr)fShGUfG> zPcMLvU#ur!j_`Ur85sr2(W4NnXFXl+Zw77&7b*|BXd|+-g_w$b4ueql)P}et(v;zv z=DMU8_Hd#G0Ci1-URmIgJCEQue)fm(nGfEBmBqR6E*!BP+_!*7?>&yA`xf!~RDu_a5ot)hm5m+8ALN93eeEFt(eq1jj?-CnHJiA*yTW zdsPMM5twO>5D!+k#kVnrKY=Xo`$j~n>tI~Ydx*s{gRKbOmL73<&d`mQ+?=??ml0>w zB3ap*2@3&01?DHpl^^}J4+Ho?xKUgPfv}{rNKPk1^ztJ=r7Y+To+I}458WJk!BkW^Y# zUXr&^0#2f*gb*r7jYVX7K>#R-@bL$3#ozy{kKv>5If-tk;R`b&%ym1s=lCk_J#he6 z*0yl^&Fd~NEks7@CN4jU@2Nqc6k=?NmqJzsLa}%Nz1!Z0a{tkU&^%30BJy=a_FVt) z9rO6RKX?$Iyk{SldmY~h|R2J+!FopUG zsF9m2j#^pp=qxT>{7HR|ywO0U0E{hL{_SmDcQbp7#geF`mX3CVe#1yOgC#C&sIs0N zOECb^7s}PY{0|=n^T)x}9NY|n9*Qe6cjRNYlY#0@`b_pi2`f_=eICq~eh*GOP7X$r z_mGqMch*3PjWW`f0{&|Ay4$_5hvMr^FpcSMjMw z?!sM1S8-`=6PIsnVWcNoeUI%bvgb=x)=eZ!aU98OC_hu)B8wVsUB?QPE5|T@_lHpK zKZe?Cv12H+BS68D>s|pobZicP?<1@DwT~XgyN@oQR~8mVTvk+*?}`0geEhCOfElk| z9%6gMrqc@}FjDs-iyE{kB3*U}^Zc>RXc5m|u^j;tyk{rQ0U_2U#J;+eVz|of@4s1C z(qK_VosV3zP4BJ1LS<*8=q-G{-2a#V;R6^AKZ$CzXc{q0`5=~sEn}+ElX@irIa~m}l8Idr672g8GB-<;lLSyftcZNPTmmoW}^sFNj zlc&DZIo$&+M4+;Box1a%FIRr-*YCq<@FP@JD_Vmn=dgzeMES}>(2(Q8jkh1!+{Qy& z>A@+I)x;k6VirqwgzgGm@qxig%&2zOQC)wt_H=bG5|@NaMBCC9{oslmc8#BG#SEGO zRQih`5-HN~&`4#RGKxCW_1}XFbNHnneHj1Mryjzw)y4eOf?#e~kR$+|vcS>(OL*Vi zx8S|E9R`^3=GApnt@uV|yQZZ8gb#)3Tk-@cMR-MQ?43IRTM9 zi&0vnG!@Gmf+e?|j1(55H!GH+yo$2>?>ju)Swl724p~>8lJ}Zq!@eZYa5k+?(*x9- zivyLix=-~&vExgY|v^!^HIx zinzwzkn?CoS-oBTG{G8aoh@bn!1Nga5K7b=D3uDMAQ?a5@ z6oeB8mvL6h*vctSNB4D`91h@Szh6_^a<(#)ocO#G%Cw%A#GhE$9*C;iV4#<|C^p z3Ha?lTf-Y0Rce@`3tQcqfoSg51Zmn;Mpt!?hIHKp*$dltTCq?Xf0tO~BKyTGC2n16 zGr|&zIKwp&7D{B3WAU+V7?Q28r45i`)VTPFOpZd^ zP#$l*ovgNLGwv5Pplu-4)8(t|=;uZj6TI_uN7v4y8f>Zj5W>(DX3oc3RWFW`!f+K& zhBpbowBp%sUUG4?p>aXC#L#y4&v)_nfBaGW&A;$o9Nsscben=6TFc*}AS})I@a~g` z@UaJP$Ah;Y!OFrMZfxyfII1ulRz8ou%yO8`&?G9?99)8bR{&71+=98=>Z^E8IsgD5 z07*naROyAL{7Kvbz*3iRa$kwh+_!|k{ee~d+@q^_--!h*&y^?(YPK=fgN|Slu+S}V z-{Co|&Jn(GZX4U9hOyYC*BMiu&w@^pr8NII8NnN0$mbc%+99oSer8PcGnn#}}|X zSNM8AYiJ{60?P{cfCumsy|yte7E5EY=ZyN0s6*+FM+n}D@y zG^oIAcawAQZV9#k$tRXA5_7K&2Cq z@TmvgdAK(>5CAF&`xbim*aNrY(R+{M;`RIS^0_Pc+KXrL_|xCPsn;)Ky+1^u-5+hh zbi4YmlbQSj<>CSKZhs_8y3C9r0}Cba?!z5?;;uzJcIyJ}Jv@iQOC2nB3zS5$nuSF6 zq_pPFhfMsO-h7wvx88pMn?uIGeRdsN!%S!Hiy%rzcSJse@Z}K`KefmVf4r5lDX~a> zr<@*A6F!+XEK0|4YK#;1mCS+#po)sl8kgN|I`Kcfd22D)`A0n5`7Z#T*8nne`w5vQ znQTlnCFBOmX_g7Dj4X$(4L2C|*$ZnIfZ>koWH{15+I#tX&OdVn$mU+Fux4w7106U@ zi%o_(#>1?pJ{kVp28E48kAeAafuH{9efagC_#lo>FsFO70t0?0#&9&kYZtHME8jYU z7tUP3%Wqu8n^)IyWqk`9{Q(BU5e9>iKWjlBy&`$@9SsP@{$rTC{SkDQR?#g9y^=8B zA*{|7xMg37yAO16|KT2f@Qy{?zS={lEW#*jisH~jI?WVBPLG*!b!&)!{zsSbJKtKz z&QRW~BW`)Gu0bXzZ7CU?%+$TCo@xtf9S5Tkv0gVzPy_n;B}7tJCk4CGNT90PZ}xiuJ7>T)nZ4*RR~bxyv_j z;o1f+U0cWNS8m|i<_^}kcCfwK$5y|O{;| zmrER7?%>E$2gmnyaAIEv2Np^!%@ruA@ZQQSMUmUN&=hB!9uZ-6p@W}&WEJ1JI>i5X zdfVvukyIVo)P0I%b$n55lPR&97uR~4M1$OZbQ7m%N52-S|NpMA$g(qKu;fk|tK;Ue z2-&Ds}KwgZ?2i?cCAHN1l1=60v(^!fn5$^ETuRHNmZ3^m*5smy-|@5wOg^Bmjg zo}qpn5jBS{21Jo8DXusK0A5o%myE6yF>i06TE7e)_Fd$VM{@U`|FnxO?CBHJL>bXM zQ8>m?lQKv-ys8`pn^YJUJT|ux0Dtf2o>;}9dY|t@w;jZ<{rLNF|H%WU)x+S_rfw>Zx>lEmegp%sr3e1w`_VjC!v}GB>mr`H zFsR?KlzIHR8^Oc0C*2B2+9Ri(LTK}GI}c4mzS`4;=k@$eqz8^ZJlG2?)PP0BkKS)9 z7U^7a+sQN*y+vqNEJeAE&iq9t?sw3eA7V7R1lrjKMo!bkHXt?8hHFcG7InJFCcRzG zQzqNvAgyoqHRSBWPld%EuXF}ge-qXE#d=QH8xsPRo+&~XajHRP$W#hDLy=mA#H_z? z_Vw;w-K#m@RX(!t8C4yISxjJW_T+&jeC{tkgvahZW>)8CA<=2WIO57-Da!(733NLJ z7Unw5w&j1m<9Gj0NGkpcvppa2#}GFo#J0tAfB%UEeD;H@_#eM~1!r%JLb<5u*?KL( zEaIl^5onsf<`6%g9&sZhvygr!y>2V2I()1*)oTmu<+rAR~Bw`Zu~HVNDShaPah)l>?1bC*F!lfJ!r?$M!zS zCP(*xAHj2f1EU*`bVU%qWanXoj~<#TmdNUV^={V%ZBKn)c^=kh7W53l4y1 zly?M{M!gIc+Dk0VKsDOt{`PsUFf6#(7-BTKfTFwxa21%V?s-Dnb{qSBVm2^fM&ka8 zG>5SFJOz+$7hWdz+GJ7s#+y|4a?Sk>R5vaGLb@ho$QLy=%Kqh$kqDIcN^&-$UR6li zmrG2xfGIFT{=y_GY5zRU2Q%=ghi=2q{Ls5`aNk_&05!}v%HL$z)k4}$UvkCaW_a1N z65?KA(dq9N3lVT&p@W}#ct1XM_aZ8WA1$#}Xt2bYE73uyOJpbf%-OQKI@Ou-dU{RR z>#1HY(G!~-*}N%O;BjR28nN(HSXkSf1`DA=(YZ#|;6hPu4U2QXbSr3X@d`SX|$Bj2YEn8`b(1@Ng%9)dZa+M(VF( zdnAebzAobV6cee|6K`3Ila70xEIdlD-6b>Q(7t*6@{c}sE`@GwkN4x&U&+j zn}MY*-efo@!P1Vd4GUG&>0P0@eOJzX_PG1yiB9(ts=;{xRq$#7fFZ{jdj4U>RGQy1 zUa-VrAC;U9(a6YD7J|$sm(%lJfg(tT`Z!`BtHp%+TcyZbOy-pWL)RUSkiHu7f>5~o zq6nQ;CAeW(f*nH`1Zr~ zysp-cyxB=T-nO9QzuiRmpF${z@Sdad_y-?7fLoTruP?_Te!{9l4^B|M0HhrR_QPr` z^KvyjX8mb5srKJ!Gu6|-w6a?)Ci59AUQ^9i=Jk;1vDZs&3Wj!@tyl_FgA3g0U8!#X zR46t9oCTGYH-zA%X|AmsIEMml7~c4zrSQj7B?0V+DadRdrj^Mwjef|3ZHzYR-3u(_ z$W(c3W%edsm@U2NcY6@P3o|;iyna#|d3f_@tti_qruM z@xV!Z>fzf^L_e-jmy`8oC-r#SyjmT9T90=UqMp{nzL^V%ZCKjkPm84>!VllIgiqYH zfVt$PY?yvp3j(Ft4pq8eg6!O}xI#9>semT{Qa1fptj=br&jdtz!4tCS?FLII0Sawb zVG%v!NO|t#ROa;p7WOiW#mJ_?0svI@0XkbB?(K+ov%4`wckxveML)bx?d|DCB}L7l zz@?CUVpbN{xq9n{vlPoDU)&g|Fq;YF3oZJ z(@cBl__HjaO^an5q0Wj0sHYtM^M_V&=l+h-7sn$usi*Hq3stA@ZdEIGt=!kO$Vm`F zaUPh~i?8)RJC&$Lx4XoG$Y$)AsVv)ap;RJHT>J)OEIvI}vT3mt#Sq>3S1%OXL%I3`DC7kf5_JCg_5SHMi#}5(&uR`IG_p;C_@z)-^ z8y~#;2s&jk)09CrkC`~*o=p?pimh$$1l!YwvLHNid;vfC-et^nGUeW=Nw!`lPD35S zmw&eHl4Ew$rrnemA$0UWL~!Z}Lwp=v_v1a((_*n7Eodyh({%rg%-d8vAkLrDV-r8Z zBJ=Dn{?u5AfNHdj(eRaicMgF3y6``J8Q{@bDm&*1RMlH0+&@#CA>;`+FY`ni5w66g z_}Y63f%6i*$Rn<2Kp5z6x{o6U-ZQl1r3lfZ)PI#hKfg)=2*TE-0IWe5K-ldN^mPR= ziDaI>BTn2d3C$-Xvf9h>uq7X z_1^b3KBHR_e*8Tv_~@;36Bsncji{d03ZxY1BFG5vp0bfSd*nS{3O3yHM5fcr*_;WB zvmT`fz)VSmy|tP2L{CqB5V?F(9;Y5j(DYaUR8)4)Gpe(=at(kG@8SJGcWw8A}BlqCe!%NCe`t{W$+sI-)J-rYwq{Xrv zDds*(Fhl1FhwCwxSa*_n=~=K$O7zprMeBSt;g+&Kv9F82_P`1bE#%8rX|_~V_H{JV zttNFh)JvAkr892sxpt_Go_Ahiy_<){J0cTe@m}ps!~y^)iqq8Tt>N(g`W67Nx({3p zH@O;}j#24-yLzHQ(hknH?PRqqB(Ews(gKKO96d)UyCm?a58k}yN!MD_hIPsjsR5Qi z&98UHh$k7wllp&x#wACtMBqaFW6UBDi)5MW;{vQIJ#>zTe}MA z?Pz+=JH$~v1Zlm1MGvy%xu140B2wu^*d6{`4=v-fpMEb+9BBXfo<9%Yn}Glv+tW`rI1}7 zamF%%qsl8=&14?RjuzD%c;Zx0RKRsUIz@qxz55ot`@|}!kgaPvJv%4cB$N9|^i2MC zkzUT$G}mtjmTAJI_9qDh!aWD)@X_1n(JgdKu#GPK)Re8yp&e&Fr)6cpIRUAol%^++ zPl%KAO>Zt1 zF@%iAtQyj&JyfSpvFEEb>;#R6$xasaEWT5b`6yM$`T!uoaeQ?VfAQg4abT(EJC=AD zo134NImV_TZ>-l8CQUDwM$XnW*KY@wX~LxTCm~@XV0EE`AGvo4$M=k6i!)oHT;tf&~$lniuj|Sm}w(IDZph zA)o^DDK5)PDt8g_R>L(=@e+!%592N7iPIDa(zqfEB%9ez#=bm)Om(E%ru04-Zll^> z14cs#i6vV|gmsly>3^RyBE;}=6;p>Nwh|j9O^g)C(Wf0{Hh$m>Dnq`xhNA*Au+S~> zlaJkjL(6j}(j>#=w_^)e)9U@S7`$x{Q=a z_N+RK!I3d;r!vLj>;$jY)?wvAMtY(Hwhq*+ShQyvZ{gDM6Ha0z+sQ(zidW22gzaRJ z?TN8414YqCQM|;X(VB*2h_~o2^ih^C0< zl$G(Yk7{cTJnAP3eps0#E(EG%iB2>; zPS17-X*_(}L45G;Bkr7Cg}7yy98VwQ;_mgZylKi}+*HQ2&!^GdD=d>u6HJGtO|Kx} z(Gv@J-?3iCAK=WD2P#HWa)lpaTJqGfRAPZ#l^&zo+kt;+5Yz!?Omc`aq|1mWZAss;802xA@STAbAzm;|-7Fo=a>5 zm~pQb4_jpT!NV<7vQL+!LP%-U+pYXm0TDojlH1)50bU)~ERLDe!Vvwhy*1`20M_gJQ1M;N=ybBMzGL%eHF7W-5=X3`5-qia8O7(-ZwV-*R; zU7zPm?cWCyl}`&PQBpoZ&NVQ0!aW0+>lXOH-G^~vbulyCoStwd345`z)GRI+g zD?Q$(vS^#qnZ$0Zmk;3Bau<)Dn8#ch;@LG3Rd+}lG9ax>)ZvfRO?0=2Ow@xEuTyMK#UC?oX zo332)(2$GFsb;D7ia^p7#m$6t&jum=0>q|801hwB<0JPT!O~o(l>~FLHViUbV;RUTLPaw1hB(?b zrEMx1B3W2`a`(H!LM>PvQ9F*%3+)LOXUDI9O3zT{fW?q%mnE~Q$ zsx-(Equ>1?sy+i0#nrtnTqhUxzHx?pN$9_lLG?VDazMBj(0*z{*>Y1L_uaCBhi*G) ze!S9}ipohf!Zr_VQrWZ%Q{%#JJkwdz+$$`5r8}*?vH;$DbPo3(=px%wn=(>W(AZR4 zxU@4twNqf6Q$H=|^!#%xGi_eW5Ym|)3%6ilKuwEC{AMg1u=q43ehv$!!opyn?4Aec zr9N->@k_Trhh+2T<@qImiZjgB3m^(#w#9Q$3J;qnTE>1_M3ZcdQ9Oa148X9DN=R23 z4g6;J_>sC%#aHjcjfMe4w zkKJ<^M^@(R3ee(Z$#u>vdn>k+<%qw{_N2u1PQucJ>tnx7LQhUby z0*KCBzPN=rq`U5aaTOJYSJ3Hx4OCQt3&x#JraeZLftcI;g*}1HuWB-YQ6E)*9jL-S zT|d;;9 zY}v7`r#TDKUSn0|R3v&iXJc1bLYjibaDn<~={R7q@s(Z{YYt25EM>7!S)uHFtpZ=e zZ`_+?A^W)#_vVH~#Z#c7@5|j~C!!snC;`bdX!b?f>?Zl-YJjT02~?v@3{4so9)&TF ziB}IRDDY#$5ZNxYsY*`@f#^zF{+%iuP|tiXlFNCVnhn08g)xx-d+Wg^+;ePSVkgd1 z?tZU^!f(}-!p*dKGsabl^b0JH-ZvYi_=buB_c@~8F^I`A(t4y)`AJ>^B{T-u` zK3-K=^j_%DPW$LMS+jqcLc}WG{atm9C744>2Wr8JJ0*) zVTRzQ8-DP%c~=e9Hm%?>WQN!J%5JyPKTHWT#`Q70UhHpL>WXQMZz7gl$YZfltj)== zP&<}-<*h>5d6s$$&z5(=<72V>K4xdjF*w^AdJku7$P+_ z!KgpB7!84)O@K#$+kzE7Sde|V!KC_e3|AB~b&;OJ8KxM7V1?)qw&od>N+wqHwJvBA z$Vij8q-3U2g|do2Xsq`+)_3!r5)a>T(Eq|jF2t+h`aZW(KIsP(ZS$&e8clk+`=&fh zDsM}BoZY?H7B{Pny-1r-Pd7Y#%N*uQTl+}2IEw47wLM@qMYd-$h{&YZFm#hNDV8}_mxA*h zf-=fhsX6DAJb}%O*st^1I?QB1w(~JFj;_q%J;ztj7AD=1a5MJ$j>9wQLf@?O&*Qds z&4OjpwA*wi!7`~{#t`0pWDZA{N>u*DGV+We{kv~#`Wg~V*E#laY6s7rpJxUt(AX7_reRr0)MXH7M7e(k zDeXu;mug zRB6DGk?VClpGk&|*DKQygt$6sF2TuaJ;xTETUJiSNNAKRy}T(``GP>}7tkb#JghZyB>#Bpye4;h@^o zW?+qg#p*$&*MXg^0?0f@Mu8DcS)g?F3a-0AFAOJ~3K~##H z2bBZqB3Hxu=hwNiWLJate`^mrCMs3QN&cr_92pz_waQRitEyo`JbmGxQAkFH>!fU2 zg&d*A^q-bYt*W|r$v+X839AcT+<9mTh1*))wg8s%-XbjK2D0I&QiICvh$5m+4EDhxzI|yLmKo^ zmd{erIWOL4T^U@lvCdN3%NRf^a5V%Eb|Oz#YwEw&Sg4Cp#SLSd3>oHC0;573Zh%3F z&<}I6=|%Wc^~iSUAVN11#K-n8;P`zpGxsV_yY;IH%dFE6 zxi|#@Czd-nx>$NyT-9@1LE~j;D;HwQZ&A~AG}cR=vQ2MxEb1XgvbK=f7S>j}>GHhA z+JprFsO+2vmCx)H!|XOP39Zfe;^$5P)!GVw;XPjOx{y1F0CgI>> z2e<9-n4OGJUY$LmOVwv)wEnwNogUyS+RsRm%~E7%{xUEH-m)) zi_Ae&GZr_8EAbn#P%&iStIXANS3h?QZDFkc#NE+0%JNT86l;mBm&0QGQ~?4RtLXx78zHWL&T(fD3b&EID7rA5s|GJ=tuyg-#{rE^Pmz!UNp{j- zdpzBuz=_obEY5XY+SAR$-t4ci-l`oE@02i!M?G$L$t<#_bcle(Zh_egE2gp^EN4hFB5NPA-faANF`2m1AINXGrS?eGshE_bXYnm~LCxq}!(28hDcjHpyJ`xG?Sc2$Oh&Zxxm%TyHg&#UA1KO4py;V9O{FwD<%- za9D^aoX(}^GvQj6+;FtOB+dc{nhUlmIGSo*Xm?m#Su|lGVFH>G3pp&*j78!r+Zs!) zSiC|-`P8NNuQYBWlQ5Qe`v;c6#HWeMuY+h?xoWD92!Q0yQ!}BSRv7icRp=Qlvj)8| z9}vmoX$K)k;bQ5VNYZy0^%O-+q)tU4GW3h}d>+kQ`DqaAjWb6>G?RCL z6&7*{EcHqp-8YYJNr>CvI1i~_oa03HR%|DmmWXB{uD2UY6Rv5n?8Uheoq}*=slY-f zeZK~lxru(95sDNM2*~UCQJKP{+G?D8{UeflgGD(&4om#Ok-bt5hi|NL`)bC*fSTQG z!vcV48%6nKRq*Nlfeyxn@k=%L7pUfW9YjxnI@e@NNg!PF8jK7;y+3y}2)#m%Z2dFO zdU?4bUvj;bh)IZ;LvQ6%OYDgiRb3RZTJjG%vVRz4I~u)D(c{!uBcdRm7fsX%6+5aN z%X1wZTbV;ym{cYXIYM|lws3tfZyS3jO{e6rlm+4FVh2lI@kF9H-ZNwGEh?|YKMyj5 z7%Xm7MvwD)b*wOqH?lp%LQ`Uizc*;ZqEAt4#{z&lR{%Uw^{|fn$J6RWYvdPbZOCQ! z>r|G{$rvH&-5k&W8}+V*(ExsWAww*-o72+{Fa%b+gUWi&(?eK5V;x7fqlpI*dDT9J zcsfF-=NUfWw&YIAUOZW^^mPd(y>MJMwa&s^2Z#5~&rH0z(6;3)H+_(suE@!zo%fss zV_TS&5O2m7wzm^YNrb~oB^G;ilTMJR`X0PRk+FYnK5zhJJPsgycx3dvqe7{k*v$G; zdyNGiM^>*93r~fGwM{>oEu;KdXtpO?yoV+@4;*xlC-L+_=pj-a zPkuKUO5a;lp0A#YfOvvVPVyYC*OU8#5KbTq=jPVa4C>zh2m4oK1QZ3~;6e|}^PR{s z4&^XSgOB4+>hVr$^X(?W@8_`8E3tpB2)h~q0P>MSm?C69thO>QmtnQr`lY=}*}jHG zo2i~YWo)-teDF3c7OyD|K%*WKJ@$I3O~H`ZB!G(I6@dR^IqZ+!%^+c7;(hHyN5MpA zK=dSt`hdLL2%PtHWebLrMBZy~*)<^|T>D@NQ=WR1O>h`xrxLDP1vku{h z=ImuMjb~Q)olULh=WC<%c7p}5He)QlG&J9M9_Cc$^#T_5GKh-|ix; zNRlz*U$|6+3>Htys>5I{^WKGhQ96Feg9eUhZYGXg-&YLmTkK-KTUs)2Y4E!tbo^Ns(5A&Qjv(JEEDT`2OITSbQ8CO}4fWKQt+zs$uB9qoQzwD} zfoN;@)9A(5`kyCEu^CM43JW5e`iYgQ=cIQq=r&}jMBC9=e0r>8zOyJVpy)nF_Go@Js%M^#i&C1n_iRWaM3F&8J za!;o{tW6!8A|0Kszf|{nO2JH-n7rigD+cy2_0V%a^^@c^6LvFYFsbcv&!!1C!yBA6 zypyI%rh0b(dS!wA^93qD8dSo}jZ6<}A`A|bJ-SueB|#`pY{Zt?|35e+U|@T6y{A5mQLX&`+OY{%ZKt|*d@^qzVn84=tNUtX`Z z9;anaHS#Nbd*5Ii`Z*N&hRzXT(F*35Eg-`U zJ(uMFM$;Cgtmj^o(NoXdbF-hqV2`dT*smB^=$0m$9`45+BrwFLX5EjAz5Tqta@9~l1m_VA|TUGI-_+rmjg5N6Udry5|gtQ!naJE9& zY9`YRntbgKYwDoAVzD2k=kbPo8$FjlKWx(8yZ-BJRSa~?0`kHcp?>IVxNE-{k%<#u zd%cEh>`|P!IwyZQjhjoSEq+tn?x!}}$^f(Bn;1*jhDIKXYsnRg@)a=uZ)Z0ipXh^$IE??OI2V5HuUGhI zr(Z(7wd__f_dHqXr9YL4=%Eq?cfS7P;`%&utYGfK4VjlDcI#h0Rx6LNuhJ_&SQ>CH%dgo@#Da!feC~fJ-`0Mfo_m`WQyT zTOG8zP^Dpea*`f|+=RKVlu-|+Dt(r}8wuQ+30|lBpAfw6aTPZ2l?GN;Cljfbm0r5D z8eFAk3so$u=_%hxOfDR@(C5}y-Lk+RJo_fjUfoh<%Tcj$QY5vFHY#$rjdPaibMqvp z$MH)L9GXhfD894en!56-h7$LVP(p7JN=70zLRQ+Wo*M(XI3jMM{Q6n))Z?LsvTDMb z7*YDFT#6FU_Ya{{^q^dgy1E$u$B~S079pM|B29SS6oWaEDJ*hkiP#kWWKz_5)3Dew zZo(3XS*14?OYpX9$GW`0W%)Q4I6uJ)%Z8bWw<^o+vci|Bz%x7=ogkoay@9EZcDMkj zw*f;S46y57>bY>Kpg|rk8pv0(kr1pTL&3WZTS7#Yz~nql_V2*StPe^`C@+nCegej@ z+N!WXk*o8FLvEyF5K#5Y?vw?-@azTr;kPf;aaa+R=n9^wF>~Cw4N=%eV+o643O*(! zeXq%_(U#Q>(ioG{@pfY(P1EB9YcBn!yi9^!i#U@`Yy87wBt5tdlS z?g3@6_%>#+P(@|uX)cN{_KRxbFV*5OGx5IhcW$k2{hu$MCdTKvDn5+S=wQf@UhNh_ zi8pIS1WMZHWMjiDycsoJ^C&Q8Ov0M{)oL=i$}6Vq%aIBnyi-$WRO&S$YcjOt?(?9Z zIj*Lt{p6+{Kte9$du(Xzg2PXlgfazWrvVwR4SCRmve3vWQ+{<>RJO)yUEjIH@5UE| z>D4&OZ>S3)e`#rF()C@Jy7wI~!4RlVCUOL%yq%0CKPFj)iBvi||Ew@j`i{(~i=*cz zo1r%OdtFw(A7fBaqI2XPlqVkmbr(W@TNdf8?a5+3Q%vNIhaDfpgT@+1pznct=s(oH`{w%IpZ zs#G~ARflc-z(-L$zrdFX+AFt$$PQ^2B`Tyiy~tE>HL~$oZb-+m^^CJHo61JECple@ zV>}tOXG9)x=9yHSj83-FM^VHhD>~>LxeMJ}9|ZLleSP8<(lz~AE*eE~lpZzci7cmw zF?y3>Q4Pvht*CBAvYZ~q=q2^lydna1y3bMB`H$cI+@aRrR2vs|MZ5qoD*C6XDE=Q5 z<))=ATiJwLvV{U!;YVmpnxueGkrGm{Z%aO42%AE$2|%-j zC%jyzkf{`&U`!*e)2%&dNlRhPRQ}Ya%7Th#KLS^_C1)h7#>&1*H_Z&9){5|)ybVbWc8n zqPNh{A!-u}b1tqtcFuKU2MAbw$+I{FnZb@49?pnGMT+CctHmxp#WERvB-1@m1E(B# z7m8vNRDOO`R;M$FrwY3!-q(NizCJ*|OH_Q-9gl`O2jLVhfYiv3qTVXCNvbl+mNC%{ zq@cEsx%7fL_gsERl5}GV1fttPZmXG84pugq3g{&;-AE`&3*lZ>GytnpAr2Crj=#NMNgNZ8m)ABP(P z$}W1h+>hR!kD{1gLcId7S%M9Tv=WXklIS@%#n*hvQJg5mHFQ^4jG-{fH8M@W5r{k% zCot*9xTqo&U&V0ryXQW4Y**ecum{8o02T4;K=B1ou^tG*c8STx@BhEOw~y8IxbDM# zXXbg|dw029E|<&Y?vh+l6sstTDk!R;q(-f#s!`iQlK^Sb1WuFIQ51d9`Y?>ZD2l)c zf0g1}HLxm+!&6}{Rl@7{gy`#h(A zJagupGc)gdKlXch2YdH@o|!pk&V2pm%$biT_rjusf=Wu*;<5m_cs6DCADRx9($#vQ zEJFs$%ssUy8slY}+#tyDuu^i)y{4O`Vbdu|Ab^=dw44faj@SOJoIYOZ zWPoioKLLDI9#qV->Y8a(`ECs~z~u0~m>hi@LbvK>JkESn$nHJ>V8*4KjtR4u1~%!i zL%J=5wRuXbF>&oc?aB^XzXcsr!5^ks6k)E(P&f;U|Jx9Uleh%#k^t}QrfwY+zD6NF zMMTwxVy*v_(cGi8aX4VssM%5G_MaN~$pEJFH>f$Ih(LxK%gNP|}x6#~cl4Na*Wp6_9PN+wX zGOtvie3?q{lAo;~np0uqJWI)Yy9?!;f1_LI(5tl9BfJ18BrH!>^19f#lWtc7ydb0y%VltgpcV`(;r4%OV z8<9jCprRbIrfC7B7`-K2P!y`pui&6$qE6}UTP(>*-sdW zxvXtaDU(A_1C}M&=`)%56`!h|DQ5;s&rDO<_FbfuZWEjf z@ZzTl=;ApF@ll$rd<*Hkm{s!~h{%KcmFL=@a%Z)GHv*Qo6}NbD_R5_+3u1s=d}SJG z!H(5NEiuUeL3OK{1yP$V2jDur&wwgF9$_+Ps`A$WLn_iVd2?xG_|<%C5wcNaQ@T#3 z(oSu_p^&+}tAeK-#x>^gL}zX_8&|H7#0^2pV-ovh)F>Z1*7_bf@Tc6F|$jA&t;W#CtW z_obJ`%e8e`>Rwg64CtocLI{s?Tsen-_@6D|ymfbJfEU0^_a7R%J^Q|kfG;C-Guy#@ z!PQe$zbFM$0n(sNMP;^ztEzQS#3(?)rn_KDS6l6L6`wFJtf7#VP=HX8k8yAa3IK&R z>|{1tRz6UXdDulWDLb1N>3nAK(8!}2V@PFztZCatBtMvWK9(kzjd?2TBww#AVL5+f zH1g!fl8ogc;!~E{)}@7%ykkTLjdD(ZVb$|B9Yl7-#V z)@9+w)A+&w5V{!!e0jQO^}8>B?AA-7?E;q&cmd#hAG&GIJbVoyJ_`ziYe>O`#Dl| zST$Dzp}y8o>!{q7Gd`!r&)!6k#WI6LQ`2tLMZ(D-9Ilvta((^B=lpN0;F&a+ef%0pQQxa@%PN@iU0=c@PZ%nNFKJ zY;}?<_o~E&AUB-ZM(?*&VKf0GK043~pjmAq6C_p=M{iNf3vL47nT!hT@Y~qz)+@c$ zOuQ^~Xx>WxlD2vk9X4yG@EWwp&XNzgAuZ%GP{2=do#sr5Lc{bIK5a6CgJ`R^+;V2oc zJlpqR%ZQw2pVR85=#IV>(>J{fVdbX8!t@9NtbS+PDVJeJ!tx}ar}OB;CVq*DE!$|z zYa!iEWLe<08rGDBT7i^4`Vcp5~d!wEH`rgS`@5x?<(K7HfJt!B3c^r?vw{|m6yF! zUc1}hjOmfLg1VI)$CeQnvgMru;973jq{w;7(RnMrZH*%UE;bjm0@Mu9rrWVBo_b5p z-8|Tmr3@TrM(YUi>jZw{!Y};AOJ-sacgcYl01)XUy6~?Mx)=Ru4(<4mSF06d&-}>6 zd+BugsX$hBh=RX{HJD{h*+eMuyW&=MvLb&inNe#f7F$L@7f=v?J796q*;}pOb>}g< zy~`qlfwk8bLNr|Eoe7nvGdmPzA|?vU%+H9Vi;=}slh^cPw$0ge2&9UkONV?sThFeI zdRaA28Yxnn+D`Z93O&dN=6Yd(0^-5rm>&6a>Fxy;Cfnol2x$bh;BLcA@pxg(;8WD8 zeI+y1lTZ){J0^PGy$?E|;B(>KDF{ zHA!2hnKzkqCDcSFQh8j(G-UExrpXsf9;`W}*@?;h`3239USmw;zFYi$;=-ejiP!d1b-en8Xe;N_m-5q#|t<(!Ee zr)uq1s}x6NKFh&}Tv*{f#xKF3NYBe7+oZI|yTPXdIOQvveBKWY1q_!CMo{rb!+rW0 zI2H9%2i3~T<-no{N_}X3=XwzS4#b6=)Er@DC8##)XtkbHn@!7~h&=U)8Sv9?eb=`N3{0RVpXZvyklcMv9jfDqH63KT#q9tw%o9!cAAU<$AT za+`qXVF3|UT&pSzUmLBomMI7eRuD|bH*$*RS1xcVYifu}+R3fVR^v`)2EDYVXgVO% z|CMg+@8r`md8)O~6hE6_Oj(mDdC^Ku{#;3G@6GQSeAS{XnrNBL z@pIgyVMSAri?i+Eo*f%_D+T~LQa%p0GKv&*Wi^)wLIi|>cShv7wV|Qa)27pGjnNN{uI@(a{w2eHaXhnQ- z@hk|I4KhiW0Ho}6ExT_8?+ZlKxQ)D18JQ3r3QQ|=v&LDh>abzc_#| zV=sMj=a-ys#a&j77XV)T#BBo){nG$F0vf+RUljpW52J=sEOb>iSJ+YA#ueZyHGuKY z;Q>-Ut--AuCIvY15a`n7^lYzfB=~*4(k{ftne|Q6=Q+j9KxT+@xa31q1~*z<9v>>u z^>LgA#CGV{NNyHw>wQfrpb!O$!NRvJ~&WSUNu1V(r8ZcGl}3+kr1o@A3l9=2r0 zmxSe9pgwFNomPw2^lFLnvoFi!S8AQrv+oNF9C*1YCNc|mXrro{8X$QS{WN9nn|0KP^PkM==3VxvRs7T4`=qL8Zlg&Fl9IXh0L-5 zgQ~n*byeD;gR8;}9;TD%F<{dHEstblE&FO7v`yLNX{(g4?ew{QXkHc6T*aAmii0%* ze1^`}qaQK}2p!_VyD_=rt?5LX8j!Lb+G*V;EEmRtFf{0-nDR&sSGJ{dbk(e}&??QK z>JCxbbgciy$ddb`8S>3eAt9(`(dTkej(~yU^hu)d`~AkonWhkz*j*9eT|d7z=RnUO zOdmp+oK<@-iVlwo49Rww(q?3N2O1y;|74ozCk;w~8S~^9w=_?1}gl)wJfbX zB%c9sau%U`kO|M8Keu*Cj|MMt%gXAKyE}jF`}?ple*pdLZ-D#pGErNsY0j*pX*ptA zn_Kpji4{t_s%Md^+t$y(P(xHH$_Vq zhLVlIh7XBN64DB7N{*k>8a>h{?CET|Oa0{NoCYLYKE=XkSx2@GlN?SpGi7S!wdr}= zI~7{f^1AXiUEdyT`_Zoi+cwA*I7sfa{EPIHxkR^S1a!CFiHSXtmIrdn)a1<$D5j2u z)(d3hwHojxa&Dk1ZP!=~DDolJi*p|Co3f-Rj5PQ)(sZ&=T%$PsU(kj7Pki#ua~CP# zmfaQ6c*VW^iKFKzga;^gPXRP9mxxAJLT>UU8)~Mqq+Q)ki=CF|cpJMb0q#~aH0WL8 zrN2Imk8*pN*L1kxKPoJglrrV*uV&OFOD!ztTmD>L+3+$NW6rnPQU%eBt;Qz4l+($q zMJu#gbIwb-Rm+qa!A;|(4neA!!_+5CS~i_(24LTf=AQGE*xfBHLZ;9K*Zy6>c?fu*lPu&EGtF zU6i1oiigSzv*#fvUE5Ms46$w2e;Is=K~R-J(U1l345&1Ww|M|_`G$+*Kgeu>9LNT7 zQ>J>Vu;`>x{QA%+08@LeX&KoJu!dWXqC5PS`j)W@Vk%C}F!f>Y(TrvDX5_B3UC`I@ zE0X{<9<40QWwf2kVjVBC3U4k808oghDa1bj=$R#KE{nV3z`L<$dJaUti*EXRpm@$t zE7KH@Y=<^YsGMFgOdJ6xc;K_~6Uf@dFbDGax9s$W4-xXsST?;7GEa%?u+V{!@4#H7 z*Vc03;@FRIYDc%gY8U|~&qkYNV=FGx&#zSd=?N~Qy~}{9+}nd0(Redg8c3>JBdgw~ z_393juoKdYS^%~ww94B-oTF9#K3D1Ep8zVe7OB>&i)%b?8N2-+OppEfe2IBEVB}x+ zAFUiu#c>X^ePt|T)ipgGnXnS)n+RPy=|g5KqLz($ucoI3U-IRCQL>cr$b_}pQuUYW zl;Syb)8FF|es{Kaaz(XW;3@#`sgK-_jj;9_h`yXm4udotVC-DXx_G0&AYm|P0w@8A zi6ek*YnN@MvETVX@E2dZw!Hv=VZBqs&ZD~5x~$?RW(Ot*PsKi9!!z@6J8uoW)iC9~ z2;;h6*bd|y>3UiLGCPA&t3ZGw#=htm-4;-rzIG1SyZY=N|R&vL0Io&5yp zGr(Hem52XKlEm>%LYrR={$*;Y$-}@V;Z5{7RH8HXGVsnnE{pQR6MsUi;YZvYiPMyO2W8!253O+o&wRAH)#E} zS3Z0gSK3{1;01tJK5^eLfSv(`2Pt&v_8+NSP6~~+1tMz`@Celc)umYUmOhye)yF4X;!3Nmfr+HFc3k@5Y-G0#=d3?*Ci83O%y~k_-i?l2O$_FCy zpp_bw)Pa;ES^lQ&*$ZnB0>Xi#=nmftOjeQiD+^f$6ISyJOkF1-%JR~Dms`fnkXz6Z z;I@ScCsDp7z1;e^pvwnDY9z&5)>X}&@-=m;@-=? zXSe093h)BJ$xj`dalj)8@n0guLe=Mt3BIveFw%t(r^A(6Of?-ZbquXes=&v_X?O|9 zH2_SLRV^!bOpwv6Qdh~B%p`vSBd2X>xKm~p$Gv%GLRt&`MA|;m%QX0z}WD++rm|qu`%c(9uO~-Y2lS2lDhI> zcNRV5Rx@`&4@rKFb}6maOzZ4!3gu@zg~?Oa4$Vzr%|>m@Nr5rPDZ_0UHsq@u(BGf zTBse=7A<7i(p;LgB2Gz{rQt?i(Drk3Ulv)oElZImD6CPOJjyZr_7M6ja!_KCyQ;u@ z>fN_vn0C)2PQFNS@=VoOOh+OEP)^GMGN4H(OTO+`<6F&Ar)UD$Zu(e#KvQ{`p5}io zy`)a1pYm%T;7}W=)$ANY>HDtRAs_O{TB4!zx0^;51K? zlNATh&e?_HYTFaHX>yA2ZPoJ3W#7^%9fw*L^Sp?96REcCwk-=aAGgGl;^Y~0-52|5 z_x$(&qa)jv^-{X43cUF7Rq)!{I@9n3V)rG)2^LR8P@-5FQ2*BRasN)KOEQXWND`jF zSLbk58tlBv4xiYREr*nj%E(Yne_>;Gpi$hr>cDAq`QmO+72kdoMccdxO@40dTD^dy zkuz1Djh+Vr(!(1ZXdwXxBQmcUhcBGG0* zW+2{t7?VTyjG)UkFT?es!y1wJw7#6c!trGF;^)Nxv$C zvTU^c@{L-O2h#L1&n#b&=T`m!3^2waqrqrCrH(!CeEyzw?W3w&+YU?~D2+b$hP5nP zDu2nV#^DA}rQ%u2CNr(W44n+1QXama zETZ${ryENvw>)%F%$jO(=taqrvRa1Zckbi$d{QR3fzDbs@n7g_fZ{@;o zBTl|f6xS=YB|nnUdg?06)8!_AuT|MDPe8u$3P>6!0d^Zy#_kGl3(qXNp_2eNNEC9z zoH(*LKsG*AwC4MbNxMrhNg`QYUYz7&1BUiNbENilVOS{`W_mlyn?Q~9J_FfmmdiSE zl@U7%Uo0cN z((c*<-ayY1g$F2f&%)e#;D7C7sh3qu&eVX#1GbUdQY$UR2Sed2zR2dt(qHN7Y>{7mV z01>-!mcuA&l*3I1#I9^*we5hkcD>MqyuVkn@5{kl!UEzgcc43T574dX z6^bHzda1tcMLxgHFwi(KN*spD&M;pU_JdDO&xE-$DF^=suu7i*kQeOi0GNkV6R{17 zHF)k=7Q2Q+3uLjpfB|9hEQk0YhwyAuysPA{Dd3%V=hyq8e++T*dBo{SK(tJ&C$XXi zfD-U><8bw-9cqlD;2`1NJZt_p3sFFsuJX|4qu>D3No@L5(iO6Dj+Ht*8L%*@>GvuZ zd2xzH5$0gpv;=_)w7vVnq~y^Sn$DhDpMNjM~K%W35m@GjcG z^1Kvf00v?=ZW%j#A1F@TOv@F$)D?>$?{g@hZc1EQ^?klILc!P@JBiF!@{`=rM$i^(Eq6ad%At?<@D;hwtwjW z;JMUXThCkJeX+7gv#sJtlL*~ubdxV~41auT&wO_B=iamp2`|69rhpd!@ZmS(Tz~#7 z1HVIY@<*Vc-#*HNoh@>w+7((1d(h5$h3UwwUD*Om#v>T*OlT~KSm1Y*tvGQ0{ur); zugB@6hqBVL_#4f}$(Y47vXjYMrNtZVUeYtaxmklTqyR%oZ_B2x2cPdsE{6oC!RNg%3Zcf(< zGd_tYFOda4{xamFB95V>1$223UBYu2-R82G`p?a$tlvZv!WzZNAA#{Z=lJ!r_(#VZ zjIXY{_J9|_*`L367!Xb%#4l2}@+2sXmlI1nT{zYj_QMRPE$1t;0%!uNF;#+Wqi(hw za%B2TD94NhR}E6X-`Ik8-KQ*l2_Bj!ou+$0DI|7I8oeu5`vFZ71&cI(3TyR!f6LuF8?% z&Dj*Up?8y5Z{A-p!VN4lt z@^&f{cVYS(U~C3u9CmW)2Q+Q|#_6NI^nGl!%B$W=r}@uu@>Z~7I-2t0^eCoWE?n@b z=xsSxe#W0wR^6&4a!dYb`BpPq2AcC+#1fW|((;&w+iI41L3w5o6+!g!BOX83v;Pis z2ao41W6M51dCi2Ge3s9fxrKDvG%g*_tgmRJ8fdcmHN?q38F>EG$xq#NMZPAn z6L(E&yyCw9sbh2QFFXwjpGBOU0MSr-U;gL zEo0@ulR}fT18BBu>-vGF?dcZrv|k~ig6Be6guY6-%bVb>^zm*bD@A3cA34SWnALi)w+c?d0r(vpfvFOg+ox?Y9=R^Bc3XJP@FR z3<|re32WVI#yM7rPDQ@UtK&p1GJ?om!d0D=c1wcmY1#as880f?zZFU6YO=&ToPs^bU@Es#VO2WU;)=#%Y~2_%Jy;W9dk? zsF|=ubXbsH$#d(nNbOVh)0rU##K{YY-DmrN$6mkf!Zo#nq2;c5;Qh1z_CB2IXdSV8 zI80U^qS&1pL&_GLT#2%P>yV<^RC{t|M9tu5wsoZzEgDLWm$f&;Lx6&mx0O^c_!@zN z?I!0vt`I ztd>qGJxPn%#w*iP8Fc~Bc^7eE%1C9kH^OCYRJ<5Kh`Nv#C+0$;0@d8JhGlKdGIjCo zOQnjN`jOd(`fo>;a(2{;ZXGXprUm}naTg_vF=a{bqS&3H$?8LhlZQ{mVg1bCzi)?< zT{(Bn11|vJ{-fAfKldtg|IZQQ!wB(oyP8NVCO3A6nkklVo#l7W1a$K$sNBIdzJ^H> z1sD~8qA8nBm^--#11BHJ3A)56s^Ea2C5!(>-{1(saiXcj<~|mQ=PfOpk6C zVOUO=Rlp*Qgl?O%6vLk>mNwi~_$?xv_+^}BKwN_NC6NUfw~fX4Ffjk;{rc;#;{Lmr zNOA4BMT5*W>Gu8FOE@ru;~0it*9=Aq{$0x z(%4c5;IZBCkPwedx~%DW*$j$eaQ=CbNfH)5j)mnfa+P)9QkpFPEGT@}Pzg5N#h%tH@S14xEq9cf zifWkBv5Pb#4wTKx0Erv+RYp#nxQJeJshe!|iJq4HOQMXtHNMDFftAUG*C2UI=`HGr z62^^uiw@_*w9Hf)rn=fkHNU|V*^X{qxal@@2anZT#wze{<)aZ+(lun+63^!C-dmBy zxU%e9QI^GD8o5EUNnLMC7V!&lpx8Y|Av`dTbnq{p%a*Q@rO}>vFbbJ&d-8M*jWDapl?n)*CKmBzgimq& zZCV}>vz%G|ZlrJW=Q=JMZQsNdt}MKGEonfS&4 zLU21RSma1*8)g5(_G>zyLJk=@saVwVyOZi)s``|%5i(LxRm_#2D*>B4e4V&7FU6Kk!vqAxGmMgw5O zI=259rD^v1Ukmr$XkUe7F&jjrI*|gpnmSs1&E5b|(QUPZK{qGZaX`XY!kfB?&u;?P zMs7{GZV%6vUkYT3fj^wi_2MJmB9v6eWGhQFSNti$7_d`v`MyHHfm~M213?RQJWn^OLP8W zcJTc36@{Emp*0ujIQvU^NQf(Pp{0PZ?>2O|9vyctY{BV$!q9xccPRVLIgT|yv~<$t zJTFF;W*EVjNhNPVXFS{atNmKMw<8MxAjCDAtbPOC^wSuIub=qD9oOVh;H|pr9(V!Z z&M*9M8V0-<^9#Q?Y@B~ThJNps2<_JmiroMs7D!j|fR=YVBtNSQ*xe2SCYk3lFn4k( z$mKN1yk~ye1GEv_bcQ?t*)KZ)0GUhQtt_K*V`OJdK<|bvrjJ&x%8tDVycYquDVrn< zkBMhnvP~~{ayyus{A94>B1~kYID+nhqv;rMqN2j)ZAG>EzT*dX0uVM1S-Kjz^pSGD zbF>!EZOI}!rfoet>-M4Ws& zbgNH-qWxHxc`zE-g2f*P$W=-WfT&`&LEnKK+m|{f(V}o~skKbYDW?;Q6?~g~9at5E zkn2$;?Sedz5ZXVh!cm!w0p_|)bJV=>*iKWi&YVC?<;12e07AIwU|L9@>XwZ20Yl?Nl~TO?`;MIZRd` zp>Fy=4#2Z7fBf)uZPP8?4Wsdjd-)TGhauoQh~2MHH~q@;Ll)_-1eQ_R59JJ07?gF6 zJX?|@x~ain?C$a=Tg_y0%rI#RR5q~y03YQ^L_t)!t!6fTyIm^%<*iqX4i_Xk!_scK zlD?C%*(FU*TeRr{4FG`vzSXjnf4x|jO3G=nRD@aPT-1cd&nE{ZFK#-Xa$2mEyANnG zK>H3N?mrBQQR_I9nTuc~e+*ydWXjNvi7CF+6I|FbIv6?|#)j@9WU;kjirz6Po+~;) zHCM@B!?~}CEQ!BJma!X!0p0W~h~2LYMBlm5Ko;D)$@j zUv#`UYhXa?y*d_KEcOSDD61NJ$18F~t09!Ot$f%*rdEO=SxciVin1iLFH(U7Kwg8P zG%5(#69fbk+!Lk44Ki@l>16qw(NQ_(e96~?(|{t!npOLfA%^TV#>iT~7LQsESkkdV zk!rn83mkVz3}p6Ao3ax@<@!ju))FAZn{Gk85lYl>Wwo@;+%voz2`-kOW=?b}ok%>Eki%F;K$z{VQ!^6n#-VH66 z@e}B(YO8U`@v3F!Md`URF4eh4`sSGp-6&dJ-Z_j2;ig*=_ur8g(lUgYm^x?C=X${K zohA=Uh9ehzYC459cIlR~*c~)xXxg?cc@SyR)z0^kMR`D0P8t}xFr9_6gzhxr`# z4-PRrbL!*Q=i!L0xEmUH0pQT*UhY-~93#d@c|QC5=;ucOu1qW$n9S(zfztzk?eZ4< zltw#93BYRaH63k`mTSRBK`Sqk$UHbl5vCr51c)qF=1y+sqzy3kU+J)NNI7^AgPz-ZI$y6mTPq`dRa2?Gild8Eu5^JAkW z=TO%!pr->Hv`xCCQ98Pv$|CK$(6l#qtpNdMhNe+ufNKrFe%4kAF;JYGLR|SB20w5i z;^gU1+<8N4y5eql;01uapL+?n_1$46`~z&Pe~9~ydywth#kgEHZ1Jo#HMPU$+sRV6 z(^5Evkpi}AA|@RJ7_k1)^0nbwikoYAmV?(smpTicGRwMXS>ORu z;yQ0*BvvtzUih^po?6yYyMmv~kqYxPnpSeJ4^ zj}DjGkGrS9WvP86SHgTIsqmNll@nh{xh*TZn5J8n+lvZWxCpb`#!6)??A?!e^X!ydpGc>@Fg1#bNpokhu7M@ERJsMR0HDl z1mfiL9KvsL7v2z{3%o%z-t2yT9Y+!oDw)}f zT+pj#J{0(8(ywg3wj2xFuIue$(>)9dof4b*hUVEXl9r|&y+BD#H?8S>ZYyL{YxB$l zyVHeJ{-~gn^mZ(ZU$k?{*MzD3Oxy)@TMctOa~08?R`z}kM4z1xc>L6!gX{RnD&8pW z4WaR7_mNdxIP>~C2#;a1@=s{8@;E525m0QdPmklM+|&Uwwi<2AvJfp;V%JxIEXH0Hov-#HkVrBs;xXicEaghhR-si^@t3}&9 z6&iL@|LS#a*$(K{R_e{N%wx!c-2Mwr00A z7f5tf9h#ZMj%9(;*@3~dZ!_-uxW#kc&RC9%mM8n?%rnOg=V-F>IJ%Wz0>{VCpILiD zfiCby(|C0Pnw7J@0L-Lj{9J2Wo?Z(=T*@T(=*%8h!HrBGH z1glb3c}lbx1))9r5pOvH#0Xhn<5bvspd~H#$RJ|2lZ}Is>d!h5>Yf5A0-3?cS4?Ce zQjJeFRjaG&V5f_dlm@)q0>#Eo@ab(=7Lc>$%DCcr)-H#Mwr$G8(JAYNJhNYzyh^eA zBgFXIeb;^S`}f~?dvv$r-mt(607rlIYgQOd!+fk5wC%fsm)wX9Z&=<$?u!x&mxnWV;BMS=Q#5JRmd% zDkU$$)&j7RwZ)?>4+cPlu;&(pn-7f_-k2BySnU4i3dsDnuEo!~fa?FJMjVl&1WzkV z$s3TNuh>hwV_DJ@0j^muaJ5t=*qTK)!_)8EcIgZhCnwOYJOqT_8E80p@{`AI=$#A8 z+#4Er0pQMG`93hA12pu14SoM{?q}}+539E6joOQ8hPnT9vBE2WoZ>p|(Cl`|iwz}^ zma_GG zxF%l3;6VtWy|*CTeA{@5xzvNIkG`BX-A-npCxN=Ebw#%+Y_?T>NHkK*Z7qv|I44WN zd#AGaX-Wx-TXs!4D_V<)Xq~$0GYH-15hs7l>;0LR|MAfsq<-VLH$3nH0Dk>@IC^1a zHG=*M`i+mGpTCd$jROGc`IJ>*iz)Ja5?cL)1uvUyh=}-d<&FB zMhCt&P6jwLU?>VV(u4AAm>xuq5jzSVb|Ea=pFIGNWl{_*j@|z8q1w zTtps#eAwpUZqcZuEsOjdb%T7d^fKEvM~^CRwT|@jW_3EmoR%{xHs0~+9LOnH-log&Ve3Bi2n?e)rZkdk-;qgPv#FA*4k>8 zpUSa~8o%U}v+)K+14m@yr4H-+fzhL=B~wctn-2qqrI;BY7Gi9c?45O}L_XcHdUFQd7CwmbTZhq-Fo4;X(IThi$;EpmLz51fu9Z@&p0_Vc5v6L9R6RFk zam{=|y&JTqI0YuF4knYH7j6^S2An6n{Q0w&oQmZ?qE#538*8gQW zuu`$m;)bSZ$PJn8~ZVOl=pE|jg(m4#jMv}LeESsV{j9+I>%HM9VDF~Bzh zV?0M;@^28Q4}s{3MPzSuw+p-r90fYtPvT>77+xfV7ntbp*n1vGR4Fz+HvwFA-~wvt zbt~$&_oVGi?nWrt0wwe=8JQn@FtsU^E7(3g`$O{2XqSe6*~M)KdkOU*moO{G+ir_%3}3t7E&$0TmZ7Q&IxX^ucNKjjRL2? zg)7OOOuUCwB9e^bR*{%cd)POtxj~|54^L_?@0K@#lJfFQA-0v%Qd1^;6ri1L{ zio$xUnMN_wa--4OcT2N$^}lr7O0WDthvDN`EaG4KOCB0@jd&S>oP6;9P{6dS_+jsSGSKr60z*Jr)V!<*u=7!DI#>qBLSS_gtBf}%n zBL7Mv<`^(# zExMdSXEnauGFH*G!k}`NV_7wGFcDmZ?}V-~ld{J%OHG_Q1 zSZRVt{99q>aR9|N>LyPiPW~0*^x=(_c;=N49e6{YLfeVk1>SACBmdJWbSrTmhWR_e z!$&cn{g>Qt><1%g^!Bfd@mg z80Nse1Yggy2H)a2@wPh_sujcZPAmHm_KoI+Hqdw&YlXEy*}`6XLsyxO*{Wb$Ke!R|1I0avRan@JM^1LEW?b<;lv#Rn;N&&esnxbR`_H{Oe(-v@9IEADnapqar`;u!$dBc$R-K92O)g5HC9 z4WL91JWO+_ zVWQz>X|!VqP??f(huWx67Pl3)8BfwW+7?}r9*kC8D7c$B1L*@ocaCEBB)XLcLHut{ zeEi6{Ewb5lh1&(*i*$E>;RmZUpT84*|F^-z2Y9~zRt)3L2h}XR7HibA(HrKZR%PRr z|JbZpD~Q>tz5`wpuUxdty)VaQ!%_?(mW2YNda~MpVS(Gr<`}s&RL&82q0f*(3!v5g z2zzfyha=2w1a|6fr{=H&kxa$Kue={Y<;puE+#LjNZtlu&p)5AXnU7St-$H1F;XB#rj0`4ihZ03RvNlKQ#em!DmBA{3wLPG~CK(GscDJ!?VTSw8%Vn!*r?U9l$#jr3gR%y})a5{-dkvxcCMGMN1>-56&d;2`{olV~FSof=Zdc>I zgzm1-yb{pSec<7zFyHvw++TPXhGDNeWo9*CjL-fsh1ZT-nlKy-tDUmJ606A;S~N_> zbD19Cx0;!B*|M@AP|IG**3y3UM4pu^0rARB2zzb;#h5u6Xf(Xt2vWr@HNP_cX5ri8 zSMx>{|7yrWmG45CWZ({Qjk@X6h?Cz3g+~c^?xh>>GwWB&?E>#5c1IpKjo4!^4gK3N z%>N$u8$X49ejGfkg5@KdgTpOAr`qxkpaNrZ;5ecn1A51-1+n~J4Tiqssn(Zm&~~7( z@<<%E)8Be)8wn4Wzw%U?^7Q5f0QTWnZygh! zMLFfC;^UK97BoI+$(d2L++}wRlswHgwkFSQ%Tj}Qm5pnVeLic5k;X8fD zYo|WEyLfgf-7fH6MtA%RukGWZe;?0h{}tvN@8f>szEPW8q#4TB07k$pfjMtPW2*@( zK#XC=t~iuhX7O%8P3*Jo_%kQ)EwpMYvZL|&S-RECEyzhYfu;hw%QoM)^fs=&nsiq9 z78rT+2o-UvB(jHY!ScHe7SNfrs&p!z7GQTOOCy@|2?M(Bd5Y7=5hs7hA%6Yk`)}J_ zJiAP8*T#D}UDutXIeiU*pXDz87=`c)+;6-S{k&u5plc*nQ`s}fy2mBZ4b+sNt&?~g{FHD_Uuoe>tWPDRit0| zP$_&BXKP!=lz?iCuTZN_(`&8^=&CIU%SFjn+?~p@C>moxoXjb9Pa{k|Lm@svae4~f zboU^{Wpuk5?-g>>2VMw=`e-P-$!H-o56y;4t_8NMz2*rn5s>-Sm~GmBU#%Vs-%QP4?x7bA;rTLhX@2(MAM`YnX`WyJ1luf*3+ z&p!I5T|4d-aJ#^Jh25Kf^IVu;xbRk<91u)w==6T<2~P>7j#4Otf4 zMe}3mo;}K=kq4axbS8cUS(c}hrU96e_vXEhrR+YeOvmxu!l8-n$dY}=g|jmhCQqT8 ze3jzlk&e3OzW1?Pc6TaVLAMLMSHa!=z}YZfGdaHxegA&$=Re8w3qOWozSr+e02F#< z8<#(&=D=8@=P-<0(6X|rpvNfbRZEhnK1>(QtXx^EW(DAJ5U`nGH0`|rsM`bDyB`R_ zZ0YiLPtG^Ksg(J-WzGA3QcuWI%ciQ>5zKe~Tc%e}&G7kICtd(&_HC=By{75#9G`;EVee&eUPpS>OZ z`~aA<{k_`KWjmI=a{&t49^@1RcYdoGn`k;1I6uc?0e@p}rpYR3b>D)!7vQu-13@`P z3#Lm=uuW%q`em!SL+JTBzig(`34a~9+Sjc&bn7khv}LkAS%5I75YHk`o<*E|mAdX< zgThHrSbOQC2ddoex+~*$f%h75$N%TorWoe;a6kVE9%ety{rp`R=7%xffEILiFRR?5 z$*Sh6ott&qY2xcw*DXS_dDqUJK`bd*88FpoY;e|Z7bCV0mR9{}x(BqfZ?x$$AS!wr z{0d0TDmh?3k?yD6fZtAL{e`E2Mv-k(Su9kekOgkd%*#?jyp_d1ma;71xl|S!Kw(Z4 zUq$SmrZD*`h3+wmlNbKK`)}D@G`j}eF7RGs?(PTAPI>nFam@R_f`0y=gZuYzKfez= zg&Z$`}NcP(qvGZ0t}3^X(&$n_4|BF&DZTru={)`dy=Lr3u$&bZjyHX zf^2}_dSRXyA!U>2B0BZY-X_h_{90TZB;d(vrPx}SE zTL|6%ju;-tWbcb7K63Le;9f&+7kIBhcgF)Sg}BkJhJlZOhqq!FK8T_JPk25%#N2ld zQHupPyGrs?1;7Lf7K9D2>Yzo|#uKDX_kgB*M*wozRcoJF7I>^diFrA_nyw71 zwdEGiI(*qJV~gY{={8NP$j?2QV)t#t$)5o92zArvU;5asyV~tF?skFq zI&pXX#tHC7w~zYaUi8Czcx zr15`y!SW>K$XFl`+Hes95r826*YC3Bpme&#= zr8w;AvaFeJN=LcA?CikvEb*8CiUW1sa|qp&pzv=I zzcxc$mK(eSZ+#uVzix*kE<7#vEt0dxZ_4YCH?Z z#yPDQU@FZQUSp(g3R>L9sf1a>Ch}oeeDoln?xP|ssk?bV$ z7RiNn61&5p)Wp;NX`!{6w+tkU=zyHQQ+Uk|5RBUGYZSU~Bg7{_;ZG2|7YCx(cpBHg zzx(xv>&oo{@Ac?@^9%ss*!l)S->)+FN6^pT3m$%)`}un@4EKTi{ouY+%{Rd*dmzc4 zIgFcob>)8x3i6u)s=C#TM$k=~Zi;32!a%GCwL;G>oWFyh98i|i$^VnCu2jQ0JbpNrd>Ppzst2eqorzbFbh>^Wh)8rOx-RyRO_W@ZL!7DaQ2d z9EZMrwhOb_UVz`m^VvJW{a?h;zl;0%z2M;hc$gwzQ3L^Tftd0 zUC^Ri#un?P%foi+wx6E*+Hf7RN{ZUgawrZDD_9RiVa62BP>9bX#HSJB|Ao*!8@llA zAx_s{{d24H+07BVpG&`S+%E9m!0zyGyoT<=g(>Fq0|Y*fjg5DL=YI*HckwWP2Zmu4 zL$7y4h(?)qByci2i0#v9ecAa#765=a8Fw&*h&HG#nt*Fq<0gVF(OufG+F3I~rB3U% zkj>MfI3R>|3f;57@H9gFQ-tX=On4F9pb2i&VV>xEJ3dd;xXaI$EB07sWIRy;g zrEcZ_1cj%DZu0H(E8*;Gt7~)o%+bwiaM#@+ZWnmpi0+{u;MfHUG&|P~2nT5xjuZE9 z<$?bK4fFc|9`^|k!+sXCL|!;+0B#ira3%qZ1_r-sYgBM=Mbk0-SxUiQ9`)S_a1@Lhm=6@c;@KzAr%l-Tqfc+RY z_UYjWxyq7&Fab?hk++O7^2a&zr-?v%cQAl@3zxGj8!dCQ?POvqL0V5E!*l1dm?b*s zfPe(FqkVP^B2ehgGH@25J4HM^&mnx5LihgycoAJZF${bblhw5gpxF;606w_?Qq|zD zyAIth@b0?N{q_&W-@WH>=c&D5o_9PuKcx`&6ZjD3^J7fmUTm!YhY0Z=^z-99Y#c%C zR*6D~&~<6cSOBH7!gYC05ZKx~3v($S53-C#=l~a9s%55|k06++H zP@IGNb?PRkK;Z@S^A|DM^M8Z+1;p+o1E;y~&&KKA87R(Qy7g6HZOn1`gf){3=4X7wc>L5yD-xvU)Fow}9v_>Q;`S-#CDNV?R^a3m#T6 z466XTw5Kej-4ylmyc$o_ds?Wq%lIDmNocupa%gTgxV zu!cA}OL1}r^VvxzoS<&?-+|}PBMc`fuAb$MjdSzK>I@+cr#^Da?m>oKx2f9&-d(pn z_lIi$_2b`PA2;s5`JuJY^RRCi`a_tl9{>-B(M=B1eB(Fm>d||!gemBkPV;;_6y5}5)IOD!Q$Mf^+ z6sI$qtj;;aIVLOfmxt59hmKvPg57mDn*09&N`eLU7W*&=00000NkvXXu0mjfPlT%e literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs index c033e3165..aa16c64ad 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -13,6 +13,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal const string IndexImagePath = "Images\\index.png"; internal const string ExcludeFromIndexImagePath = "Images\\excludeindexpath.png"; internal const string ExplorerIconImagePath = "Images\\explorer.png"; + internal const string DifferentUserIconImagePath = "Images\\user.png"; internal const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory"; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 79641fe87..845b43c54 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; @@ -117,7 +117,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search return false; if (settings.IndexSearchExcludedSubdirectoryPaths - .Any(x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).StartsWith(x.Path))) + .Any(x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath) + .StartsWith(x.Path, StringComparison.OrdinalIgnoreCase))) return false; return indexSearch.PathIsIndexed(locationPath); From 407864beb72a7c3a2ebb9bfe58aa00c647bd78c0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 8 Jun 2020 18:41:59 +1000 Subject: [PATCH 117/142] add option to open Indexing Options in context menu and settings page --- .../ContextMenu.cs | 38 +++++++++++++++++- .../Flow.Launcher.Plugin.Explorer.csproj | 4 ++ .../Images/windowsindexingoptions.png | Bin 0 -> 3772 bytes .../Search/Constants.cs | 3 ++ .../ViewModels/SettingsViewModel.cs | 17 ++++++-- .../Views/ExplorerSettings.xaml | 19 ++++++--- .../Views/ExplorerSettings.xaml.cs | 5 +++ 7 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Images/windowsindexingoptions.png diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 49318cc86..02195db98 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -38,6 +38,8 @@ namespace Flow.Launcher.Plugin.Explorer contextMenus.Add(CreateOpenContainingFolderResult(record)); + contextMenus.Add(CreateOpenWindowsIndexingOptions(record)); + if (record.ShowIndexState) contextMenus.Add(new Result {Title = "From index search: " + (record.WindowsIndexed ? "Yes" : "No"), SubTitle = "Location: " + record.FullPath, @@ -186,13 +188,13 @@ namespace Flow.Launcher.Plugin.Explorer } catch (Exception e) { - var message = $"Fail to editor for file at {record.FullPath}"; + var message = $"Failed to open editor for file at {record.FullPath}"; LogException(message, e); Context.API.ShowMsg(message); return false; } }, - IcoPath = editorPath + IcoPath = Constants.FileImagePath }; } @@ -225,6 +227,38 @@ namespace Flow.Launcher.Plugin.Explorer }; } + private Result CreateOpenWindowsIndexingOptions(SearchResult record) + { + return new Result + { + Title = "Open Windows Indexing Options", + SubTitle = "Manage indexed files and folders", + Action = _ => + { + try + { + var psi = new ProcessStartInfo + { + FileName = "control.exe", + UseShellExecute = true, + Arguments = "srchadmin.dll" + }; + + Process.Start(psi); + return true; + } + catch (Exception e) + { + var message = $"Failed to open Windows Indexing Options"; + LogException(message, e); + Context.API.ShowMsg(message); + return false; + } + }, + IcoPath = Constants.IndexingOptionsIconImagePath + }; + } + public void LogException(string message, Exception e) { Log.Exception($"|Flow.Launcher.Plugin.Folder.ContextMenu|{message}", e); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 0cbe94d86..3025f8fb7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -56,6 +56,10 @@ PreserveNewest + + + PreserveNewest + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Images/windowsindexingoptions.png b/Plugins/Flow.Launcher.Plugin.Explorer/Images/windowsindexingoptions.png new file mode 100644 index 0000000000000000000000000000000000000000..78a015715ed228d84d4a2066a2f42d7e827efbd3 GIT binary patch literal 3772 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 mc0wd{3wPp9e6wQFqW=N1$a(d|v)2s(0000 Settings.QuickFolderAccessLinks.Remove(selectedRow); internal void RemoveFolderLinkFromExcludedIndexPaths(FolderLink selectedRow) => Settings.IndexSearchExcludedSubdirectoryPaths.Remove(selectedRow); + + internal void OpenWindowsIndexingOptions() + { + var psi = new ProcessStartInfo + { + FileName = "control.exe", + UseShellExecute = true, + Arguments = Constants.WindowsIndexingOptions + }; + + Process.Start(psi); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml index 9e04c8b8f..b2f999111 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml @@ -44,10 +44,19 @@ - - From 2209bb269d1464f20d9ea892b884140b11ea49e3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Jun 2020 18:52:11 +1000 Subject: [PATCH 133/142] Hotkey tab window add close window on Esc key press --- Flow.Launcher/CustomQueryHotkeySetting.xaml | 6 ++++++ .../CustomQueryHotkeySetting.xaml.cs | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index 61b36c2d9..5f4cdff19 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -6,6 +6,12 @@ ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Title="Custom Plugin Hotkey" Height="200" Width="674.766"> + + + + + + diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 441bba509..bf5c7d769 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; +using Flow.Launcher.Core.Resource; +using Flow.Launcher.Infrastructure.Hotkey; +using Flow.Launcher.Infrastructure.UserSettings; +using NHotkey; +using NHotkey.Wpf; +using System; using System.Collections.ObjectModel; using System.Linq; using System.Windows; -using NHotkey; -using NHotkey.Wpf; -using Flow.Launcher.Core.Resource; -using Flow.Launcher.Infrastructure.Hotkey; -using Flow.Launcher.Infrastructure.UserSettings; +using System.Windows.Input; namespace Flow.Launcher { @@ -125,5 +125,10 @@ namespace Flow.Launcher MessageBox.Show(errorMsg); } } + + private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e) + { + Close(); + } } } From 1a6b056ad0b5a4eb2e479401869468910cd3132b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Jun 2020 18:57:10 +1000 Subject: [PATCH 134/142] Add text for current action keyword --- Flow.Launcher/ActionKeywords.xaml | 2 +- Flow.Launcher/Languages/en.xaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ActionKeywords.xaml b/Flow.Launcher/ActionKeywords.xaml index d0ab9a433..9d032efd9 100644 --- a/Flow.Launcher/ActionKeywords.xaml +++ b/Flow.Launcher/ActionKeywords.xaml @@ -19,7 +19,7 @@ + HorizontalAlignment="Left" Text="{DynamicResource currentActionKeywords}" /> diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 60cd4f2ad..70079095e 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -41,6 +41,7 @@ Find more plugins Disable Action keyword: + Current action keyword: New action keyword: Plugin Directory Author From 79f1b7eaca5b539ff1ef65a8c553fc619f7c5768 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Jun 2020 19:53:15 +1000 Subject: [PATCH 135/142] update build badge to AppVeypr --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23dde0a1d..c98aefb6f 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Flow Launcher ============= ![Maintenance](https://img.shields.io/maintenance/yes/2020) +[![Build status](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true&retina=true)](https://ci.appveyor.com/project/JohnTheGr8/flow-launcher/branch/dev) [![Github All Releases](https://img.shields.io/github/downloads/Flow-Launcher/Flow.Launcher/total.svg)](https://github.com/Flow-Launcher/Flow.Launcher/releases) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/Flow-Launcher/Flow.Launcher)](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) ![GitHub Release Date](https://img.shields.io/github/release-date/Flow-Launcher/Flow.Launcher) -[![Build Status](https://dev.azure.com/Flow-Launcher/Flow.Launcher/_apis/build/status/Flow.Launcher?branchName=master)](https://dev.azure.com/Flow-Launcher/Flow.Launcher/_build/latest?definitionId=1&branchName=master) Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at being more than an app launcher, it searches, integrates and expands on functionalities. Flow will continue to evolve, designed to be open and built with the community at heart. From 617ef3b05d55317c9c25102618200d195b0b0ca0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 20 Jun 2020 14:26:13 +1000 Subject: [PATCH 136/142] fix environment variable search not showing up in Explorer --- .../Search/EnvironmentVariables.cs | 5 ++++- .../Search/SearchManager.cs | 20 +++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index 5ba76bd7d..977b9423e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -10,7 +10,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search { internal static bool IsEnvironmentVariableSearch(string search) { - return LoadEnvironmentStringPaths().Count > 0 && search.StartsWith("%") && !search.Substring(1).Contains("%"); + return LoadEnvironmentStringPaths().Count > 0 + && search.StartsWith("%") + && search != "%%" + && !search.Contains("\\"); } internal static Dictionary LoadEnvironmentStringPaths() diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 4d3f11967..eb4dceda4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -39,19 +39,23 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (quickFolderLinks.Count > 0) return quickFolderLinks; - if (string.IsNullOrEmpty(query.Search)) + if (string.IsNullOrEmpty(querySearch)) return results; - if (!FilesFolders.IsLocationPathString(querySearch)) - return WindowsIndexFilesAndFoldersSearch(query, querySearch); + var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch); - var locationPath = query.Search; - - if (EnvironmentVariables.IsEnvironmentVariableSearch(locationPath)) - return EnvironmentVariables.GetEnvironmentStringPathSuggestions(locationPath, query, context); + if (isEnvironmentVariable) + return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query, context); // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ - if (locationPath.Substring(1).Contains("%")) + var isEnvironmentVariablePath = querySearch.Substring(1).Contains("%\\"); + + if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariable && !isEnvironmentVariablePath) + return WindowsIndexFilesAndFoldersSearch(query, querySearch); + + var locationPath = querySearch; + + if (isEnvironmentVariablePath) locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath); if (!FilesFolders.LocationExists(FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) From 9ad3539f9deba427e30ec70aaa1c8cb42c4b7b06 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 20 Jun 2020 15:07:41 +1000 Subject: [PATCH 137/142] update readme and authors --- README.md | 3 +++ Scripts/flowlauncher.nuspec | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c98aefb6f..53b212284 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be ![The Flow](https://user-images.githubusercontent.com/26427004/82151677-fa9c7100-989f-11ea-9143-81de60aaf07d.gif) - Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse. +- Support search using environment variable paths - Run batch and PowerShell commands as Administrator or a different user. - Support languages from Chinese to Italian and more. - Support of wide range of plugins. @@ -51,6 +52,8 @@ We welcome all contributions. If you are unsure of a change you want to make, si You will find the main goals of flow placed under Projects board, so feel free to contribute on that. If you would like to make small incremental changes, feel free to do so as well. +Get in touch if you like to join the Flow-Launcher Team and help build this great tool. + **Question/Suggestion** Yes please, submit an issue to let us know. diff --git a/Scripts/flowlauncher.nuspec b/Scripts/flowlauncher.nuspec index a4831f4db..6c5deb86b 100644 --- a/Scripts/flowlauncher.nuspec +++ b/Scripts/flowlauncher.nuspec @@ -4,7 +4,7 @@ FlowLauncher Flow Launcher $version$ - happlebao, Jeremy Wu + Flow-Launcher Team https://github.com/Flow-Launcher/Flow.Launcher https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher/master/Flow.Launcher/Images/app.png false From d47a1ed5a1d5209796e3b8d3ceb281bc1edfefb2 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Jun 2020 05:02:42 +1000 Subject: [PATCH 138/142] remove unnecessary conditional variable in if- isEnvironmentVariable --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index eb4dceda4..f814962ba 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -50,7 +50,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ var isEnvironmentVariablePath = querySearch.Substring(1).Contains("%\\"); - if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariable && !isEnvironmentVariablePath) + if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariablePath) return WindowsIndexFilesAndFoldersSearch(query, querySearch); var locationPath = querySearch; From 10d45cc9cd8215ce83f0960ce1eaf99e4aa761b2 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Jun 2020 05:16:22 +1000 Subject: [PATCH 139/142] remove maxwidth on action keyword display in Settings' plugin tab --- Flow.Launcher/SettingWindow.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 1708a5172..b58f5457d 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -175,7 +175,7 @@ + Margin="20 0 0 0"/> Date: Mon, 22 Jun 2020 05:55:55 +1000 Subject: [PATCH 140/142] settings window- update init time translation on language change --- Flow.Launcher/Languages/da.xaml | 2 +- Flow.Launcher/Languages/de.xaml | 2 +- Flow.Launcher/Languages/en.xaml | 2 +- Flow.Launcher/Languages/fr.xaml | 2 +- Flow.Launcher/Languages/it.xaml | 2 +- Flow.Launcher/Languages/ja.xaml | 2 +- Flow.Launcher/Languages/ko.xaml | 2 +- Flow.Launcher/Languages/nb-NO.xaml | 2 +- Flow.Launcher/Languages/nl.xaml | 2 +- Flow.Launcher/Languages/pl.xaml | 2 +- Flow.Launcher/Languages/pt-br.xaml | 2 +- Flow.Launcher/Languages/ru.xaml | 2 +- Flow.Launcher/Languages/sk.xaml | 2 +- Flow.Launcher/Languages/sr.xaml | 2 +- Flow.Launcher/Languages/tr.xaml | 2 +- Flow.Launcher/Languages/uk-UA.xaml | 2 +- Flow.Launcher/Languages/zh-cn.xaml | 2 +- Flow.Launcher/Languages/zh-tw.xaml | 2 +- Flow.Launcher/SettingWindow.xaml | 3 ++- Flow.Launcher/ViewModel/PluginViewModel.cs | 2 +- 20 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml index 7c1e10c8c..6b9151cd0 100644 --- a/Flow.Launcher/Languages/da.xaml +++ b/Flow.Launcher/Languages/da.xaml @@ -36,7 +36,7 @@ Nøgleord Plugin bibliotek Forfatter - Initaliseringstid: {0}ms + Initaliseringstid: Søgetid: {0}ms diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml index 5ad8a8092..bddd9c8c9 100644 --- a/Flow.Launcher/Languages/de.xaml +++ b/Flow.Launcher/Languages/de.xaml @@ -36,7 +36,7 @@ Aktionsschlüsselwörter Pluginordner Autor - Initialisierungszeit: {0}ms + Initialisierungszeit: Abfragezeit: {0}ms diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 70079095e..6807983df 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -45,7 +45,7 @@ New action keyword: Plugin Directory Author - Init time: {0}ms + Init time: Query time: {0}ms diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml index 7e8e4533f..749cf24ff 100644 --- a/Flow.Launcher/Languages/fr.xaml +++ b/Flow.Launcher/Languages/fr.xaml @@ -40,7 +40,7 @@ Mot-clé d'action : Répertoire Auteur - Chargement : {0}ms + Chargement : Utilisation : {0}ms diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml index 0120e7299..77c16fafa 100644 --- a/Flow.Launcher/Languages/it.xaml +++ b/Flow.Launcher/Languages/it.xaml @@ -40,7 +40,7 @@ Parole chiave Cartella Plugin Autore - Tempo di avvio: {0}ms + Tempo di avvio: Tempo ricerca: {0}ms diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml index 9ef340b43..36d3d1184 100644 --- a/Flow.Launcher/Languages/ja.xaml +++ b/Flow.Launcher/Languages/ja.xaml @@ -41,7 +41,7 @@ キーワード プラグイン・ディレクトリ 作者 - 初期化時間: {0}ms + 初期化時間: クエリ時間: {0}ms diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index e4ae6afa5..9bd6de201 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -40,7 +40,7 @@ 액션 키워드 플러그인 디렉토리 저자 - 초기화 시간: {0}ms + 초기화 시간: 쿼리 시간: {0}ms diff --git a/Flow.Launcher/Languages/nb-NO.xaml b/Flow.Launcher/Languages/nb-NO.xaml index 45374eb71..4a41d540e 100644 --- a/Flow.Launcher/Languages/nb-NO.xaml +++ b/Flow.Launcher/Languages/nb-NO.xaml @@ -40,7 +40,7 @@ Handlingsnøkkelord Utvidelseskatalog Forfatter - Oppstartstid: {0}ms + Oppstartstid: Spørringstid: {0}ms diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index 660b6a676..9df08631f 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -36,7 +36,7 @@ Action terfwoorden Plugin map Auteur - Init tijd: {0}ms + Init tijd: Query tijd: {0}ms diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index cb2c499b3..ae17c14c0 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -36,7 +36,7 @@ Wyzwalacze Folder wtyczki Autor - Czas ładowania: {0}ms + Czas ładowania: Czas zapytania: {0}ms diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml index 3443b33b6..dc63abe44 100644 --- a/Flow.Launcher/Languages/pt-br.xaml +++ b/Flow.Launcher/Languages/pt-br.xaml @@ -40,7 +40,7 @@ Palavras-chave de ação Diretório de Plugins Autor - Tempo de inicialização: {0}ms + Tempo de inicialização: Tempo de consulta: {0}ms diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index 0aee118c9..f0c837bbf 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -36,7 +36,7 @@ Ключевое слово Папка Автор - Инициализация: {0}ms + Инициализация: Запрос: {0}ms diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index f417e38a8..3b2e45693 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -41,7 +41,7 @@ Skratka akcie Priečinok s pluginmy Autor - Čas inic.: {0}ms + Čas inic.: Čas dopytu: {0}ms diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml index ea2da0b00..48dfc5ec7 100644 --- a/Flow.Launcher/Languages/sr.xaml +++ b/Flow.Launcher/Languages/sr.xaml @@ -40,7 +40,7 @@ Ključne reči Plugin direktorijum Autor - Vreme inicijalizacije: {0}ms + Vreme inicijalizacije: Vreme upita: {0}ms diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml index 72dba5771..aaf09394a 100644 --- a/Flow.Launcher/Languages/tr.xaml +++ b/Flow.Launcher/Languages/tr.xaml @@ -42,7 +42,7 @@ Anahtar Kelimeler Eklenti Klasörü Yapımcı - Açılış Süresi: {0}ms + Açılış Süresi: Sorgu Süresi: {0}ms diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml index 273c726e6..fb9ad5cc3 100644 --- a/Flow.Launcher/Languages/uk-UA.xaml +++ b/Flow.Launcher/Languages/uk-UA.xaml @@ -36,7 +36,7 @@ Ключове слово Директорія плагіну Автор - Ініціалізація: {0}ms + Ініціалізація: Запит: {0}ms diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index 07202c9f1..bab84caeb 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -41,7 +41,7 @@ 触发关键字 插件目录 作者 - 加载耗时 {0}ms + 加载耗时 查询耗时 {0}ms diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index 430cf0fc0..a8fd76a56 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -36,7 +36,7 @@ 觸發關鍵字 外掛資料夾 作者 - 載入耗時:{0}ms + 載入耗時: 查詢耗時:{0}ms diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index b58f5457d..b5839bac4 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -181,7 +181,8 @@ ToolTip="Change Action Keywords" Margin="5 0 0 0" Cursor="Hand" Foreground="Blue" MouseUp="OnPluginActionKeywordsClick" MaxWidth="100" /> - + + PluginPair.Metadata.ActionKeywords.Count > 1 ? Visibility.Collapsed : Visibility.Visible; - public string InitilizaTime => string.Format(_translator.GetTranslation("plugin_init_time"), PluginPair.Metadata.InitTime); + public string InitilizaTime => PluginPair.Metadata.InitTime.ToString() + "ms"; public string QueryTime => string.Format(_translator.GetTranslation("plugin_query_time"), PluginPair.Metadata.AvgQueryTime); public string ActionKeywordsText => string.Join(Query.ActionKeywordSeperater, PluginPair.Metadata.ActionKeywords); From fc37246a86a8a9ff91710921e1edbf32cb2cd561 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Jun 2020 06:16:37 +1000 Subject: [PATCH 141/142] settings window- update query time translation on language change --- Flow.Launcher/Languages/da.xaml | 2 +- Flow.Launcher/Languages/de.xaml | 2 +- Flow.Launcher/Languages/en.xaml | 2 +- Flow.Launcher/Languages/fr.xaml | 2 +- Flow.Launcher/Languages/it.xaml | 2 +- Flow.Launcher/Languages/ja.xaml | 2 +- Flow.Launcher/Languages/ko.xaml | 2 +- Flow.Launcher/Languages/nb-NO.xaml | 2 +- Flow.Launcher/Languages/nl.xaml | 2 +- Flow.Launcher/Languages/pl.xaml | 2 +- Flow.Launcher/Languages/pt-br.xaml | 2 +- Flow.Launcher/Languages/ru.xaml | 2 +- Flow.Launcher/Languages/sk.xaml | 2 +- Flow.Launcher/Languages/sr.xaml | 2 +- Flow.Launcher/Languages/tr.xaml | 2 +- Flow.Launcher/Languages/uk-UA.xaml | 2 +- Flow.Launcher/Languages/zh-cn.xaml | 2 +- Flow.Launcher/Languages/zh-tw.xaml | 2 +- Flow.Launcher/SettingWindow.xaml | 3 ++- Flow.Launcher/ViewModel/PluginViewModel.cs | 2 +- 20 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml index 6b9151cd0..91ee2616e 100644 --- a/Flow.Launcher/Languages/da.xaml +++ b/Flow.Launcher/Languages/da.xaml @@ -37,7 +37,7 @@ Plugin bibliotek Forfatter Initaliseringstid: - Søgetid: {0}ms + Søgetid: Tema diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml index bddd9c8c9..70cdbcbc8 100644 --- a/Flow.Launcher/Languages/de.xaml +++ b/Flow.Launcher/Languages/de.xaml @@ -37,7 +37,7 @@ Pluginordner Autor Initialisierungszeit: - Abfragezeit: {0}ms + Abfragezeit: Theme diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 6807983df..a28ac399e 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -46,7 +46,7 @@ Plugin Directory Author Init time: - Query time: {0}ms + Query time: Theme diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml index 749cf24ff..77537ab59 100644 --- a/Flow.Launcher/Languages/fr.xaml +++ b/Flow.Launcher/Languages/fr.xaml @@ -41,7 +41,7 @@ Répertoire Auteur Chargement : - Utilisation : {0}ms + Utilisation : Thèmes diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml index 77c16fafa..34f659ae4 100644 --- a/Flow.Launcher/Languages/it.xaml +++ b/Flow.Launcher/Languages/it.xaml @@ -41,7 +41,7 @@ Cartella Plugin Autore Tempo di avvio: - Tempo ricerca: {0}ms + Tempo ricerca: Tema diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml index 36d3d1184..b595128db 100644 --- a/Flow.Launcher/Languages/ja.xaml +++ b/Flow.Launcher/Languages/ja.xaml @@ -42,7 +42,7 @@ プラグイン・ディレクトリ 作者 初期化時間: - クエリ時間: {0}ms + クエリ時間: テーマ diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index 9bd6de201..4fbac8c6f 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -41,7 +41,7 @@ 플러그인 디렉토리 저자 초기화 시간: - 쿼리 시간: {0}ms + 쿼리 시간: 테마 diff --git a/Flow.Launcher/Languages/nb-NO.xaml b/Flow.Launcher/Languages/nb-NO.xaml index 4a41d540e..19233ffbd 100644 --- a/Flow.Launcher/Languages/nb-NO.xaml +++ b/Flow.Launcher/Languages/nb-NO.xaml @@ -41,7 +41,7 @@ Utvidelseskatalog Forfatter Oppstartstid: - Spørringstid: {0}ms + Spørringstid: Tema diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index 9df08631f..bdc92e266 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -37,7 +37,7 @@ Plugin map Auteur Init tijd: - Query tijd: {0}ms + Query tijd: Thema diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index ae17c14c0..fb67e0ce1 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -37,7 +37,7 @@ Folder wtyczki Autor Czas ładowania: - Czas zapytania: {0}ms + Czas zapytania: Skórka diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml index dc63abe44..96e84e512 100644 --- a/Flow.Launcher/Languages/pt-br.xaml +++ b/Flow.Launcher/Languages/pt-br.xaml @@ -41,7 +41,7 @@ Diretório de Plugins Autor Tempo de inicialização: - Tempo de consulta: {0}ms + Tempo de consulta: Tema diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index f0c837bbf..43dcfd716 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -37,7 +37,7 @@ Папка Автор Инициализация: - Запрос: {0}ms + Запрос: Темы diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index 3b2e45693..f62ba56c0 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -42,7 +42,7 @@ Priečinok s pluginmy Autor Čas inic.: - Čas dopytu: {0}ms + Čas dopytu: Motív diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml index 48dfc5ec7..709f5f6ce 100644 --- a/Flow.Launcher/Languages/sr.xaml +++ b/Flow.Launcher/Languages/sr.xaml @@ -41,7 +41,7 @@ Plugin direktorijum Autor Vreme inicijalizacije: - Vreme upita: {0}ms + Vreme upita: Tema diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml index aaf09394a..7a366f0bb 100644 --- a/Flow.Launcher/Languages/tr.xaml +++ b/Flow.Launcher/Languages/tr.xaml @@ -43,7 +43,7 @@ Eklenti Klasörü Yapımcı Açılış Süresi: - Sorgu Süresi: {0}ms + Sorgu Süresi: Temalar diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml index fb9ad5cc3..aabb3578b 100644 --- a/Flow.Launcher/Languages/uk-UA.xaml +++ b/Flow.Launcher/Languages/uk-UA.xaml @@ -37,7 +37,7 @@ Директорія плагіну Автор Ініціалізація: - Запит: {0}ms + Запит: Теми diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index bab84caeb..485459a43 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -42,7 +42,7 @@ 插件目录 作者 加载耗时 - 查询耗时 {0}ms + 查询耗时 主题 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index a8fd76a56..153a20da5 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -37,7 +37,7 @@ 外掛資料夾 作者 載入耗時: - 查詢耗時:{0}ms + 查詢耗時: 主題 diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index b5839bac4..acd992b6c 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -183,7 +183,8 @@ MouseUp="OnPluginActionKeywordsClick" MaxWidth="100" /> - + + diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index e6abe8e19..eb7e0054d 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -24,7 +24,7 @@ namespace Flow.Launcher.ViewModel } public Visibility ActionKeywordsVisibility => PluginPair.Metadata.ActionKeywords.Count > 1 ? Visibility.Collapsed : Visibility.Visible; public string InitilizaTime => PluginPair.Metadata.InitTime.ToString() + "ms"; - public string QueryTime => string.Format(_translator.GetTranslation("plugin_query_time"), PluginPair.Metadata.AvgQueryTime); + public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms"; public string ActionKeywordsText => string.Join(Query.ActionKeywordSeperater, PluginPair.Metadata.ActionKeywords); public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword) From d183b98e9a04e0e8bb019899283551c457d921a3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 23 Jun 2020 07:37:30 +1000 Subject: [PATCH 142/142] readd success language keyword for other controls --- Flow.Launcher/Languages/da.xaml | 1 + Flow.Launcher/Languages/de.xaml | 3 ++- Flow.Launcher/Languages/en.xaml | 3 ++- Flow.Launcher/Languages/fr.xaml | 1 + Flow.Launcher/Languages/it.xaml | 3 ++- Flow.Launcher/Languages/ja.xaml | 3 ++- Flow.Launcher/Languages/ko.xaml | 3 ++- Flow.Launcher/Languages/nb-NO.xaml | 1 + Flow.Launcher/Languages/nl.xaml | 3 ++- Flow.Launcher/Languages/pl.xaml | 3 ++- Flow.Launcher/Languages/pt-br.xaml | 3 ++- Flow.Launcher/Languages/ru.xaml | 3 ++- Flow.Launcher/Languages/sk.xaml | 3 ++- Flow.Launcher/Languages/sr.xaml | 3 ++- Flow.Launcher/Languages/tr.xaml | 3 ++- Flow.Launcher/Languages/uk-UA.xaml | 3 ++- Flow.Launcher/Languages/zh-cn.xaml | 3 ++- Flow.Launcher/Languages/zh-tw.xaml | 3 ++- 18 files changed, 33 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml index 91ee2616e..19e1951d8 100644 --- a/Flow.Launcher/Languages/da.xaml +++ b/Flow.Launcher/Languages/da.xaml @@ -92,6 +92,7 @@ Kan ikke finde det valgte plugin Nyt nøgleord må ikke være tomt Nyt nøgleord er tilknyttet et andet plugin, tilknyt venligst et andet nyt nøgeleord + Fortsæt Brug * hvis du ikke vil angive et nøgleord diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml index 70cdbcbc8..39dc9b377 100644 --- a/Flow.Launcher/Languages/de.xaml +++ b/Flow.Launcher/Languages/de.xaml @@ -1,4 +1,4 @@ - @@ -92,6 +92,7 @@ Kann das angegebene Plugin nicht finden Neues Aktionsschlüsselwort darf nicht leer sein Aktionsschlüsselwort ist schon bei einem anderen Plugin in verwendung. Bitte stellen Sie ein anderes Aktionsschlüsselwort ein. + Erfolgreich Benutzen Sie * wenn Sie ein Aktionsschlüsselwort definieren wollen. diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index a28ac399e..0bf211bd9 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -1,4 +1,4 @@ - @@ -112,6 +112,7 @@ Can't find specified plugin New Action Keyword can't be empty New Action Keywords have been assigned to another plugin, please assign other new action keyword + Success Use * if you don't want to specify an action keyword diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml index 77537ab59..a8f280bd1 100644 --- a/Flow.Launcher/Languages/fr.xaml +++ b/Flow.Launcher/Languages/fr.xaml @@ -98,6 +98,7 @@ Impossible de trouver le module spécifié Le nouveau mot-clé d'action doit être spécifié Le nouveau mot-clé d'action a été assigné à un autre module, veuillez en choisir un autre + Ajouté Saisissez * si vous ne souhaitez pas utiliser de mot-clé spécifique diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml index 34f659ae4..e85e49933 100644 --- a/Flow.Launcher/Languages/it.xaml +++ b/Flow.Launcher/Languages/it.xaml @@ -1,4 +1,4 @@ - @@ -101,6 +101,7 @@ Impossibile trovare il plugin specificato La nuova parola chiave d'azione non può essere vuota La nuova parola chiave d'azione è stata assegnata ad un altro plugin, per favore sceglierne una differente + Successo Usa * se non vuoi specificare una parola chiave d'azione diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml index b595128db..937a1e504 100644 --- a/Flow.Launcher/Languages/ja.xaml +++ b/Flow.Launcher/Languages/ja.xaml @@ -1,4 +1,4 @@ - @@ -104,6 +104,7 @@ プラグインが見つかりません 新しいアクションキーボードを空にすることはできません 新しいアクションキーボードは他のプラグインに割り当てられています。他のアクションキーボードを指定してください + 成功しました アクションキーボードを指定しない場合、* を使用してください diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index 4fbac8c6f..6c755dbae 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -1,4 +1,4 @@ - @@ -96,6 +96,7 @@ 플러그인을 찾을 수 없습니다. 새 액션 키워드를 입력하세요. 새 액션 키워드가 할당된 플러그인이 이미 있습니다. 다른 액션 키워드를 입력하세요. + 성공 액션 키워드를 지정하지 않으려면 *를 사용하세요. diff --git a/Flow.Launcher/Languages/nb-NO.xaml b/Flow.Launcher/Languages/nb-NO.xaml index 19233ffbd..19f6cc36b 100644 --- a/Flow.Launcher/Languages/nb-NO.xaml +++ b/Flow.Launcher/Languages/nb-NO.xaml @@ -101,6 +101,7 @@ Kan ikke finne den angitte utvidelsen Nytt handlingsnøkkelord kan ikke være tomt De nye handlingsnøkkelordene er tildelt en annen utvidelse, vennligst velg et annet nøkkelord + Vellykket Bruk * hvis du ikke ønsker å angi et handlingsnøkkelord diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index bdc92e266..ca7fed180 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -1,4 +1,4 @@ - @@ -92,6 +92,7 @@ Kan plugin niet vinden Nieuwe actie sneltoets moet ingevuld worden Nieuwe actie sneltoets is toegewezen aan een andere plugin, wijs een nieuwe actie sneltoets aan + Succesvol Gebruik * wanneer je geen nieuwe actie sneltoets wilt specificeren diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index fb67e0ce1..4f3042be3 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -1,4 +1,4 @@ - @@ -92,6 +92,7 @@ Nie można odnaleźć podanej wtyczki Nowy wyzwalacz nie może być pusty Ten wyzwalacz został już przypisany do innej wtyczki, musisz podać inny wyzwalacz. + Sukces Użyj * jeżeli nie chcesz podawać wyzwalacza diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml index 96e84e512..dce921ff7 100644 --- a/Flow.Launcher/Languages/pt-br.xaml +++ b/Flow.Launcher/Languages/pt-br.xaml @@ -1,4 +1,4 @@ - @@ -101,6 +101,7 @@ Não foi possível encontrar o plugin especificado A nova palavra-chave da ação não pode ser vazia A nova palavra-chave da ação já foi atribuída a outro plugin, por favor tente outra + Sucesso Use * se não quiser especificar uma palavra-chave de ação diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index 43dcfd716..d0d6ff0e5 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -1,4 +1,4 @@ - @@ -92,6 +92,7 @@ Не удалось найти заданный плагин Новая горячая клавиша не может быть пустой Новая горячая клавиша уже используется другим плагином. Пожалуйста, задайте новую + Сохранено Используйте * в случае, если вы не хотите задавать конкретную горячую клавишу diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index f62ba56c0..a2ff5926d 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -1,4 +1,4 @@ - @@ -102,6 +102,7 @@ Nepodarilo sa nájsť zadaný plugin Nová skratka pre akciu nemôže byť prázdna Nová skratka pre akciu bola priradená pre iný plugin, prosím, zvoľte inú skratku + Úspešné Použite * ak nechcete určiť skratku pre akciu diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml index 709f5f6ce..0394b398d 100644 --- a/Flow.Launcher/Languages/sr.xaml +++ b/Flow.Launcher/Languages/sr.xaml @@ -1,4 +1,4 @@ - @@ -101,6 +101,7 @@ Navedeni plugin nije moguće pronaći Prečica za novu radnju ne može da bude prazna Prečica za novu radnju je dodeljena drugom plugin-u, molim Vas dodelite drugu prečicu + Uspešno Koristite * ako ne želite da navedete prečicu za radnju diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml index 7a366f0bb..421375df9 100644 --- a/Flow.Launcher/Languages/tr.xaml +++ b/Flow.Launcher/Languages/tr.xaml @@ -1,4 +1,4 @@ - @@ -105,6 +105,7 @@ Belirtilen eklenti bulunamadı Yeni anahtar kelime boş olamaz Yeni anahtar kelime başka bir eklentiye atanmış durumda. Lütfen başka bir anahtar kelime seçin + Başarılı Anahtar kelime belirlemek istemiyorsanız * kullanın diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml index aabb3578b..b57676f8d 100644 --- a/Flow.Launcher/Languages/uk-UA.xaml +++ b/Flow.Launcher/Languages/uk-UA.xaml @@ -1,4 +1,4 @@ - @@ -92,6 +92,7 @@ Не вдалося знайти вказаний плагін Нова гаряча клавіша не може бути порожньою Нова гаряча клавіша вже використовується іншим плагіном. Будь ласка, вкажіть нову + Збережено Використовуйте * у разі, якщо ви не хочете ставити конкретну гарячу клавішу diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index 485459a43..92ce60e7f 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -1,4 +1,4 @@ - @@ -99,6 +99,7 @@ 找不到指定的插件 新触发关键字不能为空 新触发关键字已经被指派给其他插件了,请重新选择一个关键字 + 成功 如果你不想设置触发关键字,可以使用*代替 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index 153a20da5..294add207 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -1,4 +1,4 @@ - @@ -92,6 +92,7 @@ 找不到指定的外掛 新觸發關鍵字不能為空白 新觸發關鍵字已經被指派給另一外掛,請設定其他關鍵字。 + 成功 如果不想設定觸發關鍵字,可以使用*代替