summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.cpp11
-rw-r--r--binaryninjaapi.h2
-rw-r--r--binaryninjacore.h3
-rw-r--r--docs/about/open-source.md2
-rw-r--r--python/__init__.py15
-rw-r--r--python/bncompleter.py33
6 files changed, 57 insertions, 9 deletions
diff --git a/binaryninjaapi.cpp b/binaryninjaapi.cpp
index 6610782d..202019ed 100644
--- a/binaryninjaapi.cpp
+++ b/binaryninjaapi.cpp
@@ -585,3 +585,14 @@ fmt::format_context::iterator fmt::formatter<BinaryNinja::NameList>::format(cons
{
return fmt::format_to(ctx.out(), "{}", obj.GetString());
}
+
+
+std::optional<size_t> BinaryNinja::FuzzyMatchSingle(const std::string& target, const std::string& query)
+{
+ size_t result = BNFuzzyMatchSingle(target.c_str(), query.c_str());
+ if (result == 0)
+ {
+ return std::nullopt;
+ }
+ return result;
+}
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 0a1fab7c..f7362240 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -1215,6 +1215,8 @@ namespace BinaryNinja {
return result;
}
+ std::optional<size_t> FuzzyMatchSingle(const std::string& target, const std::string& query);
+
/*!
@}
*/
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 081f0d4b..6c83924a 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -37,7 +37,7 @@
// Current ABI version for linking to the core. This is incremented any time
// there are changes to the API that affect linking, including new functions,
// new types, or modifications to existing functions or types.
-#define BN_CURRENT_CORE_ABI_VERSION 65
+#define BN_CURRENT_CORE_ABI_VERSION 66
// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and
@@ -6586,6 +6586,7 @@ extern "C"
BINARYNINJACOREAPI char* BNScriptingInstanceCompleteInput(
BNScriptingInstance* instance, const char* text, uint64_t state);
BINARYNINJACOREAPI void BNStopScriptingInstance(BNScriptingInstance* instance);
+ BINARYNINJACOREAPI size_t BNFuzzyMatchSingle(const char* target, const char* query);
// Main thread actions
BINARYNINJACOREAPI void BNRegisterMainThread(BNMainThreadCallbacks* callbacks);
diff --git a/docs/about/open-source.md b/docs/about/open-source.md
index 58bf4d05..5edd31f9 100644
--- a/docs/about/open-source.md
+++ b/docs/about/open-source.md
@@ -68,6 +68,7 @@ The previous tools are used in the generation of our documentation, but are not
- [cipher] ([cipher license] - APACHE 2.0 / MIT)
- [clang] ([clang license] - APACHE 2.0)
- [clang-sys] ([clang-sys license] - APACHE 2.0)
+ - [code_fuzzy_match] ([code_fuzzy_match license] - MIT)
- [core-foundation-sys] ([core-foundation-sys license] - APACHE 2.0 / MIT)
- [core-foundation] ([core-foundation license] - APACHE 2.0 / MIT)
- [cpp_demangle] ([cpp_demangle license] - APACHE 2.0 / MIT)
@@ -417,6 +418,7 @@ Please note that we offer no support for running Binary Ninja with modified Qt l
[clang license]: https://github.com/KyleMayes/clang-rs/blob/master/LICENSE.txt
[clang-sys]: https://github.com/KyleMayes/clang-sys
[clang-sys license]: https://github.com/KyleMayes/clang-sys/blob/master/LICENSE.txt
+[code_fuzzy_match license]: https://github.com/D0ntPanic/code-fuzzy-match/blob/master/LICENSE
[core-foundation-sys]: https://github.com/servo/core-foundation-rs
[core-foundation-sys license]: https://github.com/servo/core-foundation-rs/blob/main/LICENSE-MIT
[core-foundation]: https://github.com/servo/core-foundation-rs
diff --git a/python/__init__.py b/python/__init__.py
index ec218d38..144e2643 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -461,6 +461,21 @@ class UIPluginInHeadlessError(Exception):
Exception.__init__(self, *args, **kwargs)
+def fuzzy_match_single(target, query) -> Optional[int]:
+ """
+ Fuzzy match a string against a query string. Returns a score that is higher for
+ a more confident match, or None if the query does not match the target string.
+
+ :param target: Target (larger) string
+ :param query: Query (smaller) string
+ :return: Confidence of match, or None if the string doesn't match
+ """
+ result = core.BNFuzzyMatchSingle(target, query)
+ if result == 0:
+ return None
+ return result
+
+
# Load Collaboration scripts from Enterprise (they are bundled in shipping builds)
try:
from . import collaboration
diff --git a/python/bncompleter.py b/python/bncompleter.py
index 3b632aab..4fe9ed73 100644
--- a/python/bncompleter.py
+++ b/python/bncompleter.py
@@ -41,6 +41,7 @@ Notes:
"""
import atexit
+import binaryninja
import __main__
import inspect
import sys
@@ -49,6 +50,15 @@ from typing import Optional
__all__ = ["Completer"]
+def fuzzy_match(target, query):
+ if query == "":
+ return 1
+ if binaryninja.Settings().get_bool("ui.scripting.fuzzySearch"):
+ return binaryninja.fuzzy_match_single(target, query)
+ else:
+ return query in target
+
+
def fnsignature(obj):
try:
sig = str(inspect.signature(obj))
@@ -128,24 +138,29 @@ class Completer:
seen = {"__builtins__"}
n = len(text)
for word in keyword.kwlist:
- if word[:n] == text:
+ score = fuzzy_match(word, text)
+ if score is not None:
seen.add(word)
if word in {'finally', 'try'}:
word = word + ':'
elif word not in {'False', 'None', 'True', 'break', 'continue', 'pass', 'else'}:
word = word + ' '
- matches.append(word)
+ matches.append((-score, word))
#Not sure why in the console builtins becomes a dict but this works for now.
if hasattr(__builtins__, '__dict__'): # type: ignore # remove this ignore > pyright 1.1.149
builtins = __builtins__.__dict__ # type: ignore # remove this ignore > pyright 1.1.149
else:
builtins = __builtins__ # type: ignore # remove this ignore > pyright 1.1.149
+
for nspace in [self.namespace, builtins]:
for word, val in nspace.items():
- if word[:n] == text and word not in seen:
+ score = fuzzy_match(word, text)
+ if score is not None and word not in seen:
+ # if word[:n] == text and word not in seen:
seen.add(word)
- matches.append(self._callable_postfix(val, word))
- return matches
+ matches.append((-score, self._callable_postfix(val, word)))
+ matches.sort()
+ return [match for (_, match) in matches]
def attr_matches(self, text):
"""Compute matches when text contains a dot.
@@ -186,7 +201,9 @@ class Completer:
noprefix = None
while True:
for word in words:
- if (word[:n] == attr and not (noprefix and word[:n + 1] == noprefix)):
+ score = fuzzy_match(word, attr)
+ if score is not None:
+ # if (word[:n] == attr and not (noprefix and word[:n + 1] == noprefix)):
match = f"{expr}.{word}"
try:
val = inspect.getattr_static(thisobject, word)
@@ -194,7 +211,7 @@ class Completer:
pass # Include even if attribute not set
else:
match = self._callable_postfix(val, match)
- matches.append(match)
+ matches.append((-score, match))
if matches or not noprefix:
break
if noprefix == '_':
@@ -202,7 +219,7 @@ class Completer:
else:
noprefix = None
matches.sort()
- return matches
+ return [match for (_, match) in matches]
def get_class_members(klass):