Skip to content

Instantly share code, notes, and snippets.

@zhaocai
Created October 10, 2016 06:46
Show Gist options
  • Save zhaocai/fe976e635bd5751f57917e18e21e62d5 to your computer and use it in GitHub Desktop.
Save zhaocai/fe976e635bd5751f57917e18e21e62d5 to your computer and use it in GitHub Desktop.
Everything in XYplorer
/* CUSTOMIZATIONS
-------------------------------------------------------------------------------------------------
$path: the path of the folder containing the es.exe executable, with a final
slash.
$paper: the base name of the paper folder containing the search results, without
extension;
$mode: controls how the minus sign, which introduces switches in es.exe, is
treated/escaped. Three modes are available.
Total: Everything in Xyplorer will behave as if it was a window of
Everything. The minus sign will ALWAYS be escaped with double quotes,
thus the functionality provided by es.exe switches won't be available,
however the search syntax will absolutely be the same of that of the
graphical user interface of Everything;
Smart: Everything in Xyplorer will behave as an "advanced" interface to
es.exe. The minus sign will be escaped with double quotes AS LONG AS it
is not introducing a switch, EXCEPT for -h, --help and -n when NOT
followed by a blank and one or more digits. This mode allows the use of
switches, keeping at the same time the syntax of search queries involving
actual hypens identical to the one used by Everything. In order for it to
work properly, non-ambiguous syntax must be entered, i.e. if you really
want to look for an item named "-p" (without quotes) then you must quote
it. The odds of such kind of queries are low so the attention required
for such corner cases is a more than acceptable tradeoff for the
smartness provided by this mode. Also known as "The best of the two
worlds…™";
None: the minus sign will NEVER be escaped, giving full control on how
each minus is passed to the command line. Remember: proper escaping
consists of double quotes for a "text" minus, while a "switch" minus
shall not be modified.
Whatever mode is entered that doesn't match "Total" or "Smart" is treated
as "None".
$ipc: if set to TRUE the availability of Everything via IPC is tested, i.e. the
script checks whether es.exe can successfully communicate with Everything and
obtain search results, and starts Everything service and client if not.
$diacritics: if set to TRUE it matches diacritical marks, therefore a ≠ à. This
behaviour can be temporarily overridden with the custom switch "-d".
$loc_aware: if set to TRUE it limits the scope of search to the current location
and all its subfolders. This behaviour can be temporarily overridden with the
custom switch "-l".
Note: the custom switches above must be placed at the very start of the query in
order to be recognized, and the last letter must be followed by a blank space.
They can be compounded and order doesn't matter, so "-dl " = "-ld ". However
each letter must not be repeated, so "-ll " is not valid syntax.
$unicode: if set to TRUE it tells es.exe to always output the search results to
an output file. If set to FALSE instead (default), the script will try to read
the search results from memory first, but if it finds question marks (the sign
of the presence of unrecognized Unicode characters) it will trigger the search a
second time, effectively falling back to a TRUE status of the parameter and
roughly doubling the execution time. Useful for people who rarely deal with CJK
characters and can thus gain some speed, especially on low-end machines.
*/
$path = "%ProgramW6432%\Everything\"; //"<xypath>\..\Everything\"
$paper = "Everything"; //"Everything in XYplorer"
$mode = "Total"; //"Smart"
$ipc = FALSE; //FALSE
$loc_aware = FALSE; //TRUE
$diacritics = TRUE; //TRUE
$unicode = TRUE; //FALSE
/* Main
-------------------------------------------------------------------------------------------------
First of all a test is made to make sure that es.exe can obtain results from
Everything. A simple call is made: cmd /c es * -n 1 This asks for just one file,
with no conditions at all on its name. This is easier than matching against the
four possible error messages provided by es.exe, i.e. (according to its source
code)
Everything IPC service not running. Everything IPC
window not found, IPC unavailable. failed to register
IPCTEST window class failed to create IPCTEST window
Then the output obtained via runret() is matched against a very simple
expression consisting of any capital letter of the Latin alphabet followed by a
colon and then anything. This is because Everything could return just "C:",
perfectly valid path for the system drive. If nothing can be matched it means
either Everything database is empty or there is some problem with the IPC
interface. Both can be usually solved by simply starting Everything service and
Everything client, and so the script will take care of both, repeating the steps
if necessary until it gets one result.
After that, the $query variable is declared global: doing so a search query can
be fed from an external source, for example a call via an alias. A special
boolean variable is declared as well, that will help during the validation of
the search query.
The user is asked to enter the search query if not done before, then custom
switches are searched for. The approach consists in isolating whatever matches
the regular expression "^-[dl]* " first, and then counting the occurences of
each letter in it. If a letter is found more than once the whole match is
dismissed, i.e. it is not considered a switch or a composition of switches.
Otherwise it is kept in memory and removed from the query. If what remains of
the query is not an empty, then the query itself is valid and the script can go
on (remember that an empty query tells Everything to list its full database,
which is usually huge and causes a system slowdown). While an approach with
foreach() loops might be deemed as overkill for just two letters, it can be
expanded very easily in the future (linear complexity), should more switches be
needed.
The custom switches can now perform their action. Their overriding action
against the parameters defined on top is calculated with a XOR operation. To
confine a search to the current path, this is appended to the query as:
"Shall we dance" AND path:"G:\Movies"
The current path is retrived via <curpath>. If it contains the string "paper:"
it means a paper folder is currently browsed: no standard path can be obtained,
so location-awareness is disabled and the search will be system-wide. Otherwise
the current path is processed to resolve all the junctions in it (because
Everything works with the real paths). Diacritics can be matched simply by
enclosing the query like this:
diacritics:(háček façade Pokémon)
Parentheses are not mandatory, but they can never hurt so there they are.
Then the minus sign is escaped according to the mode set above. Susbsequently,
other special characters are escaped, namely the ones that can be used as search
modifiers in Everything or in a normal regular expression and that are known to
be "problematic" in a command line. Further info on
http://forum.voidtools.com/viewtopic.php?f=5&t=1970&sid=2590f752481d94429e91191e55b6a261
and http://www.robvanderwoude.com/escapechars.php
Once a "command line-friendly" query is obtained, the proper command can be run.
There are two possibilities: the search results will either contain or not
Unicode characters.
The first case is the cleanest one, since search results can be retrieved
directly from memory via runret(). This mode is enabled when $unicode is set to
FALSE (as per user choice) AND there are no Unicode characters in the query (the
reason is pretty obvious). This is the best-guess scenario possible for a
"disk-less" execution, however there could still be Unicode characters in the
results.
If this is the case, the codepage of the console must be changed to 65001
(UTF-8) with the chcp command. This change is temporary, so this is a
portable/stealth solution that doesn't alter the system. The call to es.exe can
simply be concatenated ( && ) and the results will be written on disk to an
output file ( >> ). Cases for which this type of execution is necessary are: -
questions mark, which are the sign of the presence of Unicode characters, were
found with the "disk-less" approach; - the $results variable is empty, either
because the user set $unicode to TRUE, effectively skipping the faster execution
above, or because Unicode characters were found in the query, again effectively
skipping the faster execution.
The console will write to disk a list of matches encoded in UTF-8 without BOM.
However the absence of a BOM causes the generation of gibberish in place of CJK
characters. Therefore a little trick is required: the BOM of the future paper
folder file is written beforehand. In order to be immune to variations in system
encoding/locale/codepage, the BOM is provided to the writefile() function as a
conversion from hex values (0xEF 0xBB 0xBF).
Now the paper folder can be opened. If another, non-paper tab is focused, a new
tab will be opened, otherwise the current tab is overwritten. Finally, the
status bar will show the search query as Everything would have seen it, i.e.
with no special character escapings.
And that's all, folks.
*/
/*IPC TEST.########################################################################################
-------------------------------------------------------------------------------------------------*/
if ($ipc) {
while (runret("cmd /c es * -n 1", "$path") UnLike "[A-Z]:*") {
if (confirm("Everything in XYplorer can't work properly because Everything service and client are not running.<crlf 2>Click OK to start Everything service (a UAC prompt will appear) and client now.")) { //translatable
$time = 7;
run "cmd /c start """" /separate /wait Everything -svc", "$path", 2, 0;
while ($time > 5) {
status "Everything in XYplorer is starting Everything service, please wait $time seconds…", , "progress"; //translatable
wait 10;
$time = $time - 0.01;
};
run "Everything -startup", "$path", 0, 0;
while ($time <= 5 AND $time >= 0) {
status "Everything in XYplorer is starting Everything client, please wait $time second". ($time == 1) ? "" : "s" ."…", , "progress"; //translatable
wait 10;
$time = $time - 0.01;
};
} else {
end TRUE;
};
};
};
/*QUERY DECLARATION AND VALIDATION.################################################################
-------------------------------------------------------------------------------------------------*/
global $query; set $valid;
while (NOT $valid) {
while ($query == "") {
$query = trim(input("Everything in XYplorer", "Type your search query as you would in Everything", "Everything is awesome… [it's a movie quote ツ]", "s")); //translatable
};
$start = now("yyyy-mm-dd hh:nn:ss.ffff");
$switches = formatlist(regexmatches("$query", "^-[ld]* "), "p", "<crlf>");
if ("$switches" != "") {
foreach ($switch, "l|d") {
if (gettokenindex("$switch", "$switches", "<crlf>", "c") > 1) {
$switches = "";
break;
};
};
};
$query = trim(substr("$query", strlen(replace("$switches", "<crlf>"))));
$valid = ($query == "") ? FALSE : TRUE ;
};
/*TRANSLATION OF CUSTOM SWITCHES.##################################################################
-------------------------------------------------------------------------------------------------*/
$current_path = ("<curpath>" Like "*paper:*") ? "*" : property("#ResolveJunctionsAll", "<curpath>");
$query = ($loc_aware XOR ($switches Like "*l*")) ? "$query AND path:""$current_path""" : "$query";
$query = ($diacritics XOR ($switches Like "*d*")) ? "diacritics:$query" : "$query";
/*ESCAPING OF MINUS SIGNS AND OTHER PARTICULAR CHARACTERS.#########################################
-------------------------------------------------------------------------------------------------*/
if ("$mode" == "Smart") {
$cl = regexreplace("$query", '\B-(?!([riwps]|n (?!\D))\b)', '"-"');
} elseif ("$mode" == "Total") {
$cl = replace("$query", '-', '"-"');
};
$cl = replacelist("$cl", '< > & | ^ "', '^< ^> ^& ^| ^^ \"', " ");
$middle = now("yyyy-mm-dd hh:nn:ss.ffff");
/*NON-UNICODE, "DISK-LESS" METHOD.#################################################################
-------------------------------------------------------------------------------------------------*/
if (NOT $unicode AND NOT isunicode("$cl")) {
$results = runret("cmd /c es.exe $cl", "$path");
};
/*UNICODE METHOD WITH OUTPUT REDIRECTION.##########################################################
-------------------------------------------------------------------------------------------------*/
if ($unicode OR ($results Like "*[?]*") OR NOT isset($results)) {
writefile("<xypaper>\$paper.txt", hexdump("EFBBBF", , "ri"), , "b");
run "cmd /c chcp 65001 && es.exe $cl >> ""<xypaper>\$paper.txt""", "$path", 2, 0;
unset $results;
};
/*RESULTS PRESENTATION.############################################################################
-------------------------------------------------------------------------------------------------*/
$finish = now("yyyy-mm-dd hh:nn:ss.ffff");
if (tab("get", "path") != "paper:$paper") {
tab("new");
};
paperfolder("paper:$paper", "$results", , isset($results) ? "n" : "" . "l");
status "Everything in XYplorer [". datediff("$start", "$middle", "us")/1000 . "+" . datediff("$middle", "$finish", "us")/1000 ." ms]: $query";
@jazi4
Copy link

jazi4 commented Aug 4, 2018

Search string X Y works ok
Search string X Y Z does not

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment