summaryrefslogtreecommitdiff
path: root/python/bncompleter.py
diff options
context:
space:
mode:
authorGlenn Smith <glenn@vector35.com>2024-06-30 18:14:39 -0400
committerGlenn Smith <glenn@vector35.com>2024-07-18 16:18:01 -0400
commit3488068810307387117626066240761195266659 (patch)
tree8307a7cd9b5ced9f249f8a349fa3dbb38aa2252c /python/bncompleter.py
parent6a496b01b3e8a85c8231c949a306933e7ad58bfc (diff)
Add fuzzy completion to the python console
Co-Authored-By: Rusty Wagner <rusty@vector35.com> Co-Authored-By: Kyle Miles <kyle@vector35.com>
Diffstat (limited to 'python/bncompleter.py')
-rw-r--r--python/bncompleter.py33
1 files changed, 25 insertions, 8 deletions
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):