Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add search word candidates fuzzy match support #705

Merged
merged 2 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Using Python multithreading techniques, lsp-bridge builds a high-speed cache bet
## Installation

1. Install Emacs 28 or higher version
2. Install Python dependencies: `pip3 install epc orjson sexpdata six paramiko` (orjson is optional, orjson is based on Rust, providing faster JSON parsing performance)
2. Install Python dependencies: `pip3 install epc orjson sexpdata six paramiko rapidfuzz` (orjson is optional, orjson is based on Rust, providing faster JSON parsing performance)
3. Install Elisp dependencies:

- [markdown-mode](https://github.com/jrblevin/markdown-mode)
Expand Down Expand Up @@ -200,6 +200,8 @@ lsp-bridge first looks for the content of the first *.pub file in the `~/.ssh` d
- `acm-backend-yas-candidate-min-length`: The minimum characters to trigger yasnippet completion, default is 0
- `acm-backend-search-file-words-candidate-min-length`: The minimum characters to trigger search file words completion, default is 0
- `acm-backend-search-file-words-max-number`: Search Words completion candidate limit, default is 10
- `acm-backend-search-file-words-enable-fuzzy-match`: Search Words completion candidate fuzzy match, disable by default
- `acm-backend-search-file-words-enable-fuzzy-match-threshold`: Search Words completion candidate fuzzy match threshold, Filter out words with a ratio lower than the threshold, default is 50
- `acm-backend-codeium-candidate-min-length`: The minimum characters to trigger codeium completion, default is 0
- `acm-backend-lsp-enable-auto-import`: automatic insert import code, enable by default
- `acm-backend-lsp-candidate-max-length`: Maximum length of LSP candidate, some language, such as Java, argument list is very long, you can increase the value of this option to see clear argument list
Expand Down
4 changes: 3 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ lsp-bridge 使用 Python 多线程技术在 Emacs 和 LSP 服务器之间构建
## 安装

1. 安装 Emacs 28 及以上版本
2. 安装 Python 依赖: `pip3 install epc orjson sexpdata six paramiko` (orjson 是可选的, orjson 基于 Rust, 提供更快的 JSON 解析性能)
2. 安装 Python 依赖: `pip3 install epc orjson sexpdata six paramiko rapidfuzz` (orjson 是可选的, orjson 基于 Rust, 提供更快的 JSON 解析性能)
3. 安装 Elisp 依赖:

- [markdown-mode](https://github.com/jrblevin/markdown-mode)
Expand Down Expand Up @@ -201,6 +201,8 @@ lsp-bridge 优先从`~/.ssh`目录下找第一个 *.pub 文件的内容作为远
- `acm-backend-yas-candidate-min-length`: YaSnippet 补全最小的触发字符数, 默认是 0
- `acm-backend-search-file-words-candidate-min-length`: Search Words 补全最小的触发字符数, 默认是 0
- `acm-backend-search-file-words-max-number`: Search Words 补全候选词限制, 默认是 10
- `acm-backend-search-file-words-enable-fuzzy-match`: Search Words 补全候选词启用模糊匹配, 默认不使能
- `acm-backend-search-file-words-enable-fuzzy-match-threshold`: Search Words 补全候选词过滤, 过滤掉相似度小于阈值的候选词, 默认是50
- `acm-backend-codeium-candidate-min-length`: Codeium 补全最小的触发字符数, 默认是 0
- `acm-backend-lsp-enable-auto-import`: 支持自动导入, 默认打开
- `acm-backend-lsp-candidate-max-length`: LSP 候选词最大长度, 一些语言参数较长, 可以适当增加这个选项的值以看清楚参数列表
Expand Down
10 changes: 10 additions & 0 deletions acm/acm-backend-search-file-words.el
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@
:type 'integer
:group 'acm-backend-search-file-words)

(defcustom acm-backend-search-file-words-enable-fuzzy-match nil
"enable fuzzy match candidate."
:type 'boolean
:group 'acm-backend-search-file-words)

(defcustom acm-backend-search-file-words-enable-fuzzy-match-threshold 50
"Filter out words with a ratio lower than the threshold."
:type 'integer
:group 'acm-backend-search-file-words)

(defcustom acm-enable-search-file-words t
"Popup search words completions when this option is turn on."
:type 'boolean
Expand Down
26 changes: 16 additions & 10 deletions core/search_file_words.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ def __init__(self):
self.search_files = set()
self.search_content_dict = {}
self.search_words_thread = None

(self.max_number, ) = get_emacs_vars(["acm-backend-search-file-words-max-number"])

(self.max_number, self.fuzzy_match, self.fuzzy_match_threshold) = get_emacs_vars(["acm-backend-search-file-words-max-number", "acm-backend-search-file-words-enable-fuzzy-match", "acm-backend-search-file-words-enable-fuzzy-match-threshold"])
self.search_words_queue = queue.Queue()
self.search_words_dispatcher_thread = threading.Thread(target=self.search_dispatcher)
self.search_words_dispatcher_thread.start()
Expand Down Expand Up @@ -133,15 +131,23 @@ def search_words_from_files(self, prefix: str):
logger.error(traceback.format_exc())

def search_word(self, prefix, all_words):
match_words = list(filter(lambda word: word.lower().startswith(prefix.lower()), all_words))
candidates = []
if prefix.isupper():
candidates = list(map(lambda word: word.upper(), match_words))
if self.fuzzy_match:
from rapidfuzz import fuzz
match_words = list(map(lambda word: {'word':word, 'ratio':fuzz.ratio(prefix.lower(), word.lower())}, all_words))
match_words.sort(key=lambda x:x['ratio'], reverse=True)
for word in match_words:
if word['ratio'] >= self.fuzzy_match_threshold:
candidates.append(word['word'])
else:
break
else:
candidates = list(map(lambda word: prefix + word[len(prefix):], match_words))

candidates.sort(key=len, reverse=False)

match_words = list(filter(lambda word: word.lower().startswith(prefix.lower()), all_words))
if prefix.isupper():
candidates = list(map(lambda word: word.upper(), match_words))
else:
candidates = list(map(lambda word: prefix + word[len(prefix):], match_words))
candidates.sort(key=len, reverse=False)
return candidates

def search_dispatcher(self):
Expand Down
Loading