在 daemon 优化了异常处理行为; 为编辑器等也设置了可定义的环境变量; 更新 README;
This commit is contained in:
@@ -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<String, String> sha2PathMD = snippetManager.getFileStatus();
|
||||
if (!sha2PathMD.equals(sha2PathDB)) {
|
||||
log.info("数据库与文件目录不一致,重建索引数据库");
|
||||
List<RebuildEntity> snippets = snippetManager.listAll();
|
||||
Set<Index> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Index> indexSet) throws SQLException {
|
||||
public void rebuildIndex() throws SQLException, IOException {
|
||||
log.info("重建索引数据库");
|
||||
resetIndex();
|
||||
List<RebuildEntity> snippets = snippetManager.listAll();
|
||||
Set<Index> indexSet = snippets.stream().map(entity -> {
|
||||
Index index = new Index();
|
||||
BeanUtil.copyProperties(entity, index);
|
||||
return index;
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
for (Index index : indexSet) {
|
||||
add(index);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
MainMenu(r).run()
|
||||
|
||||
24
README.md
24
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. 解压,目录结构如下:
|
||||
> 
|
||||
|
||||
### 使用
|
||||
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)
|
||||
|
||||
|
||||
## 说明
|
||||
|
||||
BIN
doc/resource/dir.png
Normal file
BIN
doc/resource/dir.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Reference in New Issue
Block a user