diff --git a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/App.java b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/App.java index ea476a9..e6208ab 100644 --- a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/App.java +++ b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/App.java @@ -1,10 +1,8 @@ package work.slhaf.snippet; -import cn.hutool.core.bean.BeanUtil; import lombok.extern.slf4j.Slf4j; import work.slhaf.snippet.common.Constant; -import work.slhaf.snippet.entity.db.Index; -import work.slhaf.snippet.entity.file.RebuildEntity; +import work.slhaf.snippet.exception.LaunchCheckException; import work.slhaf.snippet.gateway.CodeSnippetSocketServer; import work.slhaf.snippet.service.IndexManager; import work.slhaf.snippet.service.SnippetManager; @@ -13,9 +11,6 @@ import java.io.File; import java.io.IOException; import java.sql.SQLException; import java.util.HashMap; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; @Slf4j public class App{ @@ -53,14 +48,7 @@ public class App{ HashMap sha2PathMD = snippetManager.getFileStatus(); if (!sha2PathMD.equals(sha2PathDB)) { log.info("数据库与文件目录不一致,重建索引数据库"); - List snippets = snippetManager.listAll(); - Set indexSet = snippets.stream().map(entity -> { - Index index = new Index(); - BeanUtil.copyProperties(entity, index); - return index; - }) - .collect(Collectors.toSet()); - indexManager.rebuildIndex(indexSet); + indexManager.rebuildIndex(); } log.info("索引数据库检查通过"); } @@ -82,7 +70,7 @@ public class App{ boolean ok = file.mkdirs(); if (!ok) { - throw new RuntimeException("创建目录失败: " + dir); + throw new LaunchCheckException("创建目录失败: " + dir); } else { log.info("创建目录成功: {}", dir); } @@ -91,7 +79,7 @@ public class App{ private String getEnvOrThrow(String key) { String value = System.getenv(key); - if (value == null) throw new RuntimeException("未找到环境变量: " + key); + if (value == null) throw new LaunchCheckException("未找到环境变量: " + key); return value; } diff --git a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/ActionHandleException.java b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/ActionHandleException.java new file mode 100644 index 0000000..2eb9525 --- /dev/null +++ b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/ActionHandleException.java @@ -0,0 +1,12 @@ +package work.slhaf.snippet.exception; + +public class ActionHandleException extends RuntimeException +{ + public ActionHandleException(String message) { + super(message); + } + + public ActionHandleException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/LaunchCheckException.java b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/LaunchCheckException.java new file mode 100644 index 0000000..bc8dcdc --- /dev/null +++ b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/LaunchCheckException.java @@ -0,0 +1,11 @@ +package work.slhaf.snippet.exception; + +public class LaunchCheckException extends RuntimeException{ + public LaunchCheckException(String message) { + super(message); + } + + public LaunchCheckException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/SnippetManagerException.java b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/SnippetManagerException.java new file mode 100644 index 0000000..43b7462 --- /dev/null +++ b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/exception/SnippetManagerException.java @@ -0,0 +1,11 @@ +package work.slhaf.snippet.exception; + +public class SnippetManagerException extends RuntimeException{ + public SnippetManagerException(String message) { + super(message); + } + + public SnippetManagerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/ActionHandler.java b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/ActionHandler.java index bdb45b5..6c5b4aa 100644 --- a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/ActionHandler.java +++ b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/ActionHandler.java @@ -12,6 +12,7 @@ import work.slhaf.snippet.entity.file.AddEntity; import work.slhaf.snippet.entity.file.EditEntity; import work.slhaf.snippet.entity.file.ListEntity; import work.slhaf.snippet.entity.file.MetaDataEntity; +import work.slhaf.snippet.exception.ActionHandleException; import java.io.IOException; import java.nio.file.Files; @@ -53,16 +54,22 @@ public class ActionHandler { private SocketOutputData handleDelete(String path) throws SQLException, IOException { if (path.isEmpty()) { - return new SocketOutputData(Constant.Status.FAILED, "Path不能为空!"); + throw new ActionHandleException("Path不能为空!"); + } + try { + indexManager.delete(path); + snippetManager.delete(path); + return new SocketOutputData(Constant.Status.SUCCESS, "删除成功: " + path); + } catch (Exception e) { + log.warn("代码片段删除失败,尝试重建索引"); + indexManager.rebuildIndex(); + throw new ActionHandleException("代码片段删除失败,路径: " + path, e); } - indexManager.delete(path); - snippetManager.delete(path); - return new SocketOutputData(Constant.Status.SUCCESS, "删除成功: " + path); } private SocketOutputData handleEdit(EditEntity entity) throws IOException { if (entity.checkEmpty()) { - return new SocketOutputData(Constant.Status.FAILED, "Id、Path、代码内容均不能为空!"); + throw new ActionHandleException("Id、Path、代码内容均不能为空!"); } try { snippetManager.update(entity, SnippetManager.UpdateAction.EDIT); @@ -75,14 +82,13 @@ public class ActionHandler { return new SocketOutputData(Constant.Status.SUCCESS, "文件编辑成功: " + entity.getPath()); } catch (Exception e) { snippetManager.update(entity, SnippetManager.UpdateAction.FALLBACK); - log.error("文件编辑失败, 已回滚: {}", e.getLocalizedMessage()); - return new SocketOutputData(Constant.Status.FAILED, e.getLocalizedMessage()); + throw new ActionHandleException("文件编辑失败,已回滚: " + entity, e); } } private SocketOutputData handleAdd(AddEntity entity) throws IOException { if (entity.checkEmpty()) { - return new SocketOutputData(Constant.Status.FAILED, "Language、Name、代码片段内容均不能为空!"); + throw new ActionHandleException("Language、Name、代码片段内容均不能为空!"); } Path path = Path.of(System.getenv(Constant.Property.DIR), entity.getLanguage().toLowerCase(), entity.getName() + ".md"); try { @@ -94,8 +100,7 @@ public class ActionHandler { return new SocketOutputData(Constant.Status.SUCCESS, "代码片段已添加, 路径: " + path); } catch (Exception e) { Files.deleteIfExists(path); - log.error("文件添加失败: {}", e.getLocalizedMessage()); - return new SocketOutputData(Constant.Status.FAILED, e.getLocalizedMessage()); + throw new ActionHandleException("文件添加失败: " + entity, e); } } diff --git a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/IndexManager.java b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/IndexManager.java index d9bded1..9bd3f0e 100644 --- a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/IndexManager.java +++ b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/IndexManager.java @@ -1,12 +1,16 @@ package work.slhaf.snippet.service; +import cn.hutool.core.bean.BeanUtil; import lombok.extern.slf4j.Slf4j; import work.slhaf.snippet.common.Constant; import work.slhaf.snippet.entity.db.Index; import work.slhaf.snippet.entity.file.ListEntity; +import work.slhaf.snippet.entity.file.RebuildEntity; +import java.io.IOException; import java.sql.*; import java.util.*; +import java.util.stream.Collectors; @Slf4j public class IndexManager { @@ -14,6 +18,7 @@ public class IndexManager { private static volatile IndexManager instance; private final Connection connection; + private final SnippetManager snippetManager = new SnippetManager(); public static IndexManager getInstance() throws SQLException { if (instance == null) { @@ -47,9 +52,16 @@ public class IndexManager { statement.close(); } - public void rebuildIndex(Set indexSet) throws SQLException { + public void rebuildIndex() throws SQLException, IOException { log.info("重建索引数据库"); resetIndex(); + List snippets = snippetManager.listAll(); + Set indexSet = snippets.stream().map(entity -> { + Index index = new Index(); + BeanUtil.copyProperties(entity, index); + return index; + }) + .collect(Collectors.toSet()); for (Index index : indexSet) { add(index); } diff --git a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/SnippetManager.java b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/SnippetManager.java index 3816ada..b7bc4f0 100644 --- a/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/SnippetManager.java +++ b/CodeSnippetDaemon/src/main/java/work/slhaf/snippet/service/SnippetManager.java @@ -8,6 +8,7 @@ import work.slhaf.snippet.common.Constant; import work.slhaf.snippet.entity.Snippet; import work.slhaf.snippet.entity.file.EditEntity; import work.slhaf.snippet.entity.file.RebuildEntity; +import work.slhaf.snippet.exception.SnippetManagerException; import java.io.File; import java.io.FileReader; @@ -52,7 +53,7 @@ public class SnippetManager { String markdown = snippet.toMarkdown(); File file = path.toFile(); if (file.exists()) { - throw new RuntimeException("文件已存在: " + file.getAbsolutePath()); + throw new SnippetManagerException("文件已存在: " + file.getAbsolutePath()); } file.getParentFile().mkdirs(); Files.writeString(file.toPath(), markdown, StandardCharsets.UTF_8); @@ -102,7 +103,7 @@ public class SnippetManager { public String sha(String filePath) { File file = new File(filePath); if (!file.exists()) { - throw new RuntimeException("文件不存在: " + filePath); + throw new SnippetManagerException("文件不存在: " + filePath); } return DigestUtil.sha256Hex(file); } diff --git a/CodeSnippetRofi/common/constant.py b/CodeSnippetRofi/common/constant.py index ba86237..9d1a3b0 100644 --- a/CodeSnippetRofi/common/constant.py +++ b/CodeSnippetRofi/common/constant.py @@ -2,13 +2,16 @@ import os code_snippet_dir = os.getenv("CODE_SNIPPET_DIR") code_snippet_port = os.getenv("CODE_SNIPPET_PORT") -code_snippet_rofi = os.getenv("CODE_SNIPPET_ROFI") +code_snippet_rofi = os.getenv("CODE_SNIPPET_ROFI", "rofi") +code_snippet_editor = os.getenv("CODE_SNIPPET_EDITOR","nvim") action_list = "LIST" action_add = "ADD" action_edit = "EDIT" action_delete = "DELETE" +editor_class = "code_snippet_editor" + template_add = """ ## Snippet diff --git a/CodeSnippetRofi/common/rofi.py b/CodeSnippetRofi/common/rofi.py index 3ef9137..845d89d 100644 --- a/CodeSnippetRofi/common/rofi.py +++ b/CodeSnippetRofi/common/rofi.py @@ -25,11 +25,11 @@ # import atexit -from datetime import datetime -from decimal import Decimal, InvalidOperation import signal import subprocess import time +from datetime import datetime +from decimal import Decimal, InvalidOperation # Python < 3.2 doesn't provide a context manager interface for Popen. # Let's make our own wrapper if needed. diff --git a/CodeSnippetRofi/helper/file_helper.py b/CodeSnippetRofi/helper/file_helper.py index 0044c46..81171c7 100644 --- a/CodeSnippetRofi/helper/file_helper.py +++ b/CodeSnippetRofi/helper/file_helper.py @@ -8,6 +8,8 @@ import subprocess import tempfile from pathlib import Path +from CodeSnippetRofi.common.constant import editor_class, code_snippet_editor + from common.constant import template_add, template_edit from entity.result import ActionResult from helper.api_helper import run_edit, run_add @@ -201,23 +203,23 @@ def _open_with_nvim(file_path: str) -> None: # 根据终端类型添加特定参数 if name == 'alacritty': - cmd.extend(['--class', 'floating-nvim', '-e', 'nvim', file_path]) + cmd.extend(['--class', editor_class, '-e', code_snippet_editor, file_path]) elif name == 'kitty': - cmd.extend(['--class', 'floating-nvim', 'nvim', file_path]) + cmd.extend(['--class', editor_class, code_snippet_editor, file_path]) elif name == 'foot': - cmd.extend(['-a', 'floating-nvim', 'nvim', file_path]) + cmd.extend(['-a', editor_class, code_snippet_editor, file_path]) elif name == 'wezterm': - cmd.extend(['start', '--class', 'floating-nvim', '--', 'nvim', file_path]) + cmd.extend(['start', '--class', editor_class, '--', code_snippet_editor, file_path]) elif name.startswith('gnome-terminal'): - cmd.extend(['--class=floating-nvim', '--', 'nvim', file_path]) + cmd.extend([f'--class={editor_class}', '--', code_snippet_editor, file_path]) elif name == 'konsole': - cmd.extend(['--class', 'floating-nvim', '-e', 'nvim', file_path]) + cmd.extend(['--class', editor_class, '-e', code_snippet_editor, file_path]) elif name == 'urxvt': - cmd.extend(['-name', 'floating-nvim', '-e', 'nvim', file_path]) + cmd.extend(['-name', editor_class, '-e', code_snippet_editor, file_path]) elif name == 'xterm': - cmd.extend(['-class', 'floating-nvim', '-e', 'nvim', file_path]) + cmd.extend(['-class', editor_class, '-e', code_snippet_editor, file_path]) else: - cmd.extend(['-e', 'nvim', file_path]) + cmd.extend(['-e', code_snippet_editor, file_path]) # 阻塞执行命令,直到进程结束 subprocess.run(cmd) diff --git a/CodeSnippetRofi/launcher.py b/CodeSnippetRofi/launcher.py index a4d314a..83e9f2e 100755 --- a/CodeSnippetRofi/launcher.py +++ b/CodeSnippetRofi/launcher.py @@ -1,11 +1,6 @@ -import os - -from common.constant import code_snippet_rofi from common import rofi +from common.constant import code_snippet_rofi from menu.MainMenu import MainMenu -if code_snippet_rofi is None: - r = rofi.Rofi() -else: - r = rofi.Rofi(code_snippet_rofi) +r = rofi.Rofi(code_snippet_rofi) MainMenu(r).run() diff --git a/README.md b/README.md index 16efde9..a0dc03b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CodeSnippetDaemon +~~# CodeSnippet 使用 Rofi 前端 + Java 守护进程管理代码片段,支持 Markdown 存储、跨平台同步和快速索引。 ## 项目概述 @@ -26,9 +26,17 @@ ### 前端 - 默认使用 Rofi 作为启动器,已涵盖后端所需的所有操作类型 - 借助 Python 完成复杂操作 -- 涉及到代码片段的编辑行为时,将通过调用 nvim 来编辑临时文件,编辑完毕后将发送请求至守护进程 +- 涉及到代码片段的编辑行为时,默认将通过调用 nvim 来编辑临时文件,编辑完毕后将发送请求至守护进程 -## 使用方法 +## 快速开始 +### 安装 +1. 下载RELEASE +2. 下载所需库 + - `pip install python-rofi` +2. 解压,目录结构如下: + > ![目录结构](doc/resource/dir.png) + +### 使用 1. 设置环境变量 - CODE_SNIPPET_CONF - 配置文件目录 @@ -42,11 +50,21 @@ - AI服务商提供的 base_url - CODE_SNIPPET_MODEL - 所用模型名称 + - CODE_SNIPPET_EDITOR (可选,若未指定则使用nvim) + - 所用的编辑器,默认使用nvim + - Typora 好像也可以编辑,只是会额外打开一个终端窗口 + - 其他编辑器未测试 - CODE_SNIPPET_ROFI (可选,若未指定则使用默认 rofi) - 指定的 rofi 脚本,可以添加参数,比如: `rofi -theme ~/.config/rofi/launchers/type-4/style-1.rasi` 2. 启动 Java 守护进程 + - `java -jar daemon/CodeSnippetDaemon-1.0.jar` 3. 启动 rofi 脚本 + - `python rofi/launcher.py` + +> 已将打开的编辑器窗口的class属性设置为`code_snippet_editor`,如果使用的是 Hyprland 等 wm,可以据此对打开的编辑器窗口设置所需规则 +> +> 该部分只针对一些常见的终端应用进行了设置,如有需要,可以自行编辑文件: [file_helper(第205行开始)](CodeSnippetRofi/helper/file_helper.py) ## 说明 diff --git a/doc/resource/dir.png b/doc/resource/dir.png new file mode 100644 index 0000000..c776c54 Binary files /dev/null and b/doc/resource/dir.png differ