Skip to content

Commit

Permalink
Added open file location functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverschwendener committed Feb 17, 2019
1 parent 9cfa68e commit 4e12db0
Show file tree
Hide file tree
Showing 19 changed files with 196 additions and 69 deletions.
4 changes: 4 additions & 0 deletions src/common/helpers/string-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ export class StringHelpers {

return value.charAt(0).toUpperCase() + value.slice(1);
}

public static replaceWhitespace(original: string, replaceWith: string): string {
return original.replace(/\s/g, replaceWith);
}
}
1 change: 1 addition & 0 deletions src/common/ipc-channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum IpcChannels {
search = "search",
searchResponse = "search-response",
execute = "execute",
openSearchResultLocation = "open-search-result-location",
mainWindowHasBeenHidden = "main-window-has-been-hidden",
mainWindowHasBeenShown = "main-window-has-been-shown",
userInputHasBeenResetAndMainWindowCanBeHidden = "user-input-has-been-reset-and-main-window-can-be-hidden",
Expand Down
29 changes: 10 additions & 19 deletions src/main/executors/file-path-executor.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,27 @@
import { FileHelpers } from "../helpers/file-helpers";
import { exec } from "child_process";
import osascript = require("node-osascript");

export function executeFilePathWindows(filePath: string, privileged: boolean): Promise<void> {
return privileged
? executeFilePathWindowsAsPrivileged(filePath)
: executeFilePath(`start explorer "${filePath}"`, filePath);
: executeFilePath(`start explorer "${filePath}"`);
}

export function executeFilePathMacOs(filePath: string, privileged: boolean): Promise<void> {
return privileged
? executeFilePathMacOsAsPrivileged(filePath)
: executeFilePath(`open "${filePath}"`, filePath);
: executeFilePath(`open "${filePath}"`);
}

function executeFilePath(command: string, filePath: string): Promise<void> {
function executeFilePath(command: string): Promise<void> {
return new Promise((resolve, reject) => {
FileHelpers.fileExists(filePath)
.then((exists) => {
if (exists) {
exec(command, (err) => {
if (err) {
reject(`Error while opening file: ${err}`);
} else {
resolve();
}
});
} else {
reject(`Error while executing file: File "${filePath}" does not exist.`);
}
})
.catch((err) => reject(err));
exec(command, (err) => {
if (err) {
reject(`Error while opening file: ${err}`);
} else {
resolve();
}
});
});
}

Expand Down
21 changes: 21 additions & 0 deletions src/main/executors/file-path-location-executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { exec } from "child_process";

export function executeFilePathLocationWindows(filePath: string): Promise<void> {
return executeFilePathLocation(`start explorer.exe /select,"${filePath}"`);
}

export function executeFilePathLocationMacOs(filePath: string): Promise<void> {
return executeFilePathLocation(`open -R "${filePath}"`);
}

function executeFilePathLocation(command: string): Promise<void> {
return new Promise((resolve, reject) => {
exec(command, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
6 changes: 6 additions & 0 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@ function registerAllIpcListeners() {
.catch((err) => logger.error(err));
});

ipcMain.on(IpcChannels.openSearchResultLocation, (event: Electron.Event, searchResultItem: SearchResultItem) => {
searchEngine.openLocation(searchResultItem)
.then(() => hideMainWindow())
.catch((err) => logger.error(err));
});

ipcMain.on(IpcChannels.reloadApp, () => {
reloadApp();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { convert } from "app2png";
import { FileHelpers } from "../../helpers/file-helpers";
import { exec } from "child_process";
import { ueliTempFolder } from "../../../common/helpers/ueli-helpers";
import { StringHelpers } from "../../../common/helpers/string-helpers";

export const applicationIconLocation = join(ueliTempFolder, "application-icons");
export const powershellScriptFilePath = join(ueliTempFolder, "generate-icons.ps1");

export function getApplicationIconFilePath(application: Application): string {
const fileHashName = createHash("md5").update(`${application.filePath}`).digest("hex");
return `${join(applicationIconLocation, fileHashName)}.png`;
const hash = createHash("md5").update(`${application.filePath}`).digest("hex");
const fileName = `${StringHelpers.replaceWhitespace(application.name.toLowerCase(), "-")}-${hash}`;
return `${join(applicationIconLocation, fileName)}.png`;
}

export function generateMacAppIcons(applications: Application[]): Promise<void> {
Expand Down Expand Up @@ -52,27 +54,11 @@ export function generateWindowsAppIcons(applications: Application[]): Promise<vo

let powershellScript = `
Add-Type -AssemblyName System.Drawing;
function generateIcon($filePath, $outputFilePath) {
$fileExists = Test-Path -Path $filePath;
if ($fileExists) {
$fileIsShortcut = $filePath.endsWith(".lnk");
if ($fileIsShortcut) {
try {
$sh = New-Object -ComObject WScript.Shell;
$filePath = $sh.CreateShortcut($filePath).TargetPath;
}
catch { <# continue #>}
}
$fileExists = Test-Path -Path $filePath;
if ($fileExists) {
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($filePath);
$icon.ToBitmap().save($outputFilePath, [System.Drawing.Imaging.ImageFormat]::Png);
}
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($filePath);
$icon.ToBitmap().save($outputFilePath, [System.Drawing.Imaging.ImageFormat]::Png);
}
}
`;
Expand All @@ -97,7 +83,7 @@ export function generateWindowsAppIcons(applications: Application[]): Promise<vo

function executePowershellScript(filePath: string): Promise<void> {
return new Promise((resolve, reject) => {
exec(`powershell -File ${filePath}`, (err) => {
exec(`powershell -File ${filePath}`, (err, stdout) => {
if (err) {
reject(err);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ import { IconType } from "../../../common/icon/icon-type";

export class ApplicationSearchPlugin implements SearchPlugin {
public readonly pluginType = PluginType.ApplicationSearchPlugin;
public readonly openLocationSupported = true;
private config: ApplicationSearchOptions;
private readonly applicationRepository: ApplicationRepository;
private readonly executeApplication: (executionArgument: string, privileged?: boolean) => Promise<void>;
private readonly openApplicationLocation: (filePath: string) => Promise<void>;

constructor(config: ApplicationSearchOptions,
applicationRepository: ApplicationRepository,
executeApplication: (executionArgument: string, privileged: boolean) => Promise<void>) {
executeApplication: (executionArgument: string, privileged: boolean) => Promise<void>,
openApplicationLocation: (filePath: string) => Promise<void>) {
this.config = config;
this.applicationRepository = applicationRepository;
this.executeApplication = executeApplication;
this.openApplicationLocation = openApplicationLocation;
}

public getAll(): Promise<SearchResultItem[]> {
Expand Down Expand Up @@ -47,6 +51,14 @@ export class ApplicationSearchPlugin implements SearchPlugin {
});
}

public openLocation(searchResultItem: SearchResultItem): Promise<void> {
return new Promise((resolve, reject) => {
this.openApplicationLocation(searchResultItem.executionArgument)
.then(() => resolve())
.catch((err) => reject(err));
});
}

public refreshIndex(): Promise<void> {
return new Promise((resolve, reject) => {
if (!this.isEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { EverythingSearcher } from "./everything-searcher";

export class EverythingExecutionPlugin implements ExecutionPlugin {
public pluginType: PluginType = PluginType.EverythingSearchPlugin;
public readonly openLocationSupported = true;
private config: UserConfigOptions;
private readonly filePathExecutor: (filePath: string, privileged: boolean) => Promise<void>;
private readonly filePathLocationExecutor: (filePath: string) => Promise<void>;
private readonly defaultIcon: Icon = {
parameter: `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" version="1.1">
<g id="surface1">
Expand All @@ -19,9 +21,14 @@ export class EverythingExecutionPlugin implements ExecutionPlugin {
type: IconType.SVG,
};

constructor(config: UserConfigOptions, filePathExecutor: (filePath: string, privileged: boolean) => Promise<void>) {
constructor(
config: UserConfigOptions,
filePathExecutor: (filePath: string, privileged: boolean) => Promise<void>,
filePathLocationExecutor: (filePath: string) => Promise<void>,
) {
this.config = config;
this.filePathExecutor = filePathExecutor;
this.filePathLocationExecutor = filePathLocationExecutor;
}

public isEnabled(): boolean {
Expand Down Expand Up @@ -49,6 +56,14 @@ export class EverythingExecutionPlugin implements ExecutionPlugin {
});
}

public openLocation(searchResultItem: SearchResultItem): Promise<void> {
return new Promise((resolve, reject) => {
this.filePathLocationExecutor(searchResultItem.executionArgument)
.then(() => resolve())
.catch((err) => reject(err));
});
}

public updateConfig(updatedUserConfig: UserConfigOptions): Promise<void> {
return new Promise((resolve, reject) => {
this.config = updatedUserConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { MdFindSearcher } from "./mdfind-searcher";
import { defaultErrorIcon } from "../../../common/icon/default-error-icon";

export class MdFindExecutionPlugin implements ExecutionPlugin {
public pluginType = PluginType.MdFindExecutionPlugin;
public readonly pluginType = PluginType.MdFindExecutionPlugin;
public readonly openLocationSupported = true;
private config: UserConfigOptions;
private readonly filePathExecutor: (filePath: string, privileged: boolean) => Promise<void>;
private readonly filePathLocationExecutor: (filePath: string) => Promise<void>;
private searchDelay: NodeJS.Timeout | number;
private readonly defaultIcon: Icon = {
parameter: `<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
Expand All @@ -29,9 +31,14 @@ export class MdFindExecutionPlugin implements ExecutionPlugin {
type: IconType.SVG,
};

constructor(config: UserConfigOptions, filePathExecutor: (filePath: string, privileged: boolean) => Promise<void>) {
constructor(
config: UserConfigOptions,
filePathExecutor: (filePath: string, privileged: boolean) => Promise<void>,
filePathLocationExecutor: (filePath: string) => Promise<void>,
) {
this.config = config;
this.filePathExecutor = filePathExecutor;
this.filePathLocationExecutor = filePathLocationExecutor;
}

public isValidUserInput(userInput: string): boolean {
Expand Down Expand Up @@ -72,6 +79,14 @@ export class MdFindExecutionPlugin implements ExecutionPlugin {
});
}

public openLocation(searchResultItem: SearchResultItem): Promise<void> {
return new Promise((resolve, reject) => {
this.filePathLocationExecutor(searchResultItem.executionArgument)
.then(() => resolve())
.catch((err) => reject(err));
});
}

public updateConfig(updatedConfig: UserConfigOptions): Promise<void> {
return new Promise((resolve) => {
this.config = updatedConfig;
Expand Down
17 changes: 17 additions & 0 deletions src/main/plugins/shorcuts-search-plugin/shortcuts-search-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@ interface ExecutionArgumentDecodeResult {

export class ShortcutsSearchPlugin implements SearchPlugin {
public readonly pluginType = PluginType.ShortcutsSearchPlugin;
public readonly openLocationSupported = true;
private config: ShortcutOptions;
private readonly urlExecutor: (url: string) => Promise<void>;
private readonly filePathExecutor: (filePath: string, privileged: boolean) => Promise<void>;
private readonly filePathLocationExecutor: (filePath: string) => Promise<void>;

constructor(
config: ShortcutOptions,
urlExecutor: (url: string) => Promise<void>,
filePathExecutor: (filePath: string, privileged: boolean) => Promise<void>,
filePathLocationExecutor: (filePath: string) => Promise<void>,
) {
this.config = config;
this.urlExecutor = urlExecutor;
this.filePathExecutor = filePathExecutor;
this.filePathLocationExecutor = filePathLocationExecutor;
}

public getAll(): Promise<SearchResultItem[]> {
Expand Down Expand Up @@ -67,6 +71,19 @@ export class ShortcutsSearchPlugin implements SearchPlugin {
}
}

public openLocation(searchResultItem: SearchResultItem): Promise<void> {
return new Promise((resolve, reject) => {
const decodeResult = this.decodeExecutionArgument(searchResultItem.executionArgument);
if (decodeResult.shortcutType === ShortcutType.FilePath) {
this.filePathLocationExecutor(decodeResult.executionArgument)
.then(() => resolve())
.catch((err) => reject(err));
} else {
reject(`Error while trying to open file location. "${decodeResult.executionArgument}" is not a valid file path`);
}
});
}

public isEnabled(): boolean {
return this.config.isEnabled;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { LingueeTranslator } from "./linguee-translator";

export class TranslationExecutionPlugin implements ExecutionPlugin {
public readonly pluginType = PluginType.TranslationPlugin;
public readonly openLocationSupported = false;
private config: UserConfigOptions;
private delay: NodeJS.Timeout | number;
private readonly icon: Icon = {
Expand All @@ -33,6 +34,10 @@ export class TranslationExecutionPlugin implements ExecutionPlugin {
});
}

public openLocation(searchResultItem: SearchResultItem): Promise<void> {
throw new Error("Opening location is not supported on this plugin");
}

public getSearchResults(userInput: string): Promise<SearchResultItem[]> {
return new Promise((resolve, reject) => {
const textToTranslate = userInput.replace(this.config.translationOptions.prefix, "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { TranslationManager } from "../../../common/translation/translation-mana
import { TranslationKey } from "../../../common/translation/translation-key";

export class UeliCommandSearchPlugin implements SearchPlugin {
public pluginType = PluginType.UeliCommandSearchPlugin;
public readonly pluginType = PluginType.UeliCommandSearchPlugin;
public readonly openLocationSupported = false;
private readonly translationManager: TranslationManager;
private readonly icon = `
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
Expand Down Expand Up @@ -45,11 +46,15 @@ export class UeliCommandSearchPlugin implements SearchPlugin {
ipcMain.emit(IpcChannels.ueliCommandExecuted, ueliCommand);
resolve();
} else {
reject("Invalid ueli command");
reject("Error while trying to execute ueli command: Invalid ueli command");
}
});
}

public openLocation(searchResultItem: SearchResultItem): Promise<void> {
throw new Error("Opening location is not supported on this plugin");
}

public refreshIndex(): Promise<void> {
return new Promise((resolve) => {
resolve();
Expand Down
Loading

0 comments on commit 4e12db0

Please sign in to comment.