Compare commits
3
Commits
3765e6b05d
...
03f8786d24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03f8786d24 | ||
|
|
bd20482cea | ||
|
|
1ed75ca156 |
No files matched your search
+285
-5
@@ -1092,11 +1092,12 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
"properties": {
|
"properties": {
|
||||||
"action": {
|
"action": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["get_active", "read", "open", "create_scratch", "save", "reload", "close", "list", "search", "edit"],
|
"enum": ["get_active", "read", "open", "create_scratch", "save", "reload", "close", "list", "search", "edit", "undo", "redo"],
|
||||||
"description": "Action to perform on the buffer"
|
"description": "Action to perform on the buffer"
|
||||||
},
|
},
|
||||||
"file": { "type": "string", "description": "Target file path (for open, read, search)" },
|
"file": { "type": "string", "description": "Target file path (for open, read, search)" },
|
||||||
"buf_id": { "type": "integer", "description": "Buffer ID (for close, reload, or split)" },
|
"buf_id": { "type": "integer", "description": "Buffer ID (for close, reload, or split)" },
|
||||||
|
"steps": { "type": "integer", "description": "Number of undo/redo steps to apply (default: 1)" },
|
||||||
"content": { "type": "string", "description": "Initial text content (for create_scratch)" },
|
"content": { "type": "string", "description": "Initial text content (for create_scratch)" },
|
||||||
"name": { "type": "string", "description": "Buffer display name (for create_scratch)" },
|
"name": { "type": "string", "description": "Buffer display name (for create_scratch)" },
|
||||||
"filetype": { "type": "string", "description": "Filetype syntax (for open, create_scratch)" },
|
"filetype": { "type": "string", "description": "Filetype syntax (for open, create_scratch)" },
|
||||||
@@ -1245,6 +1246,43 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
},
|
},
|
||||||
"required": ["action"]
|
"required": ["action"]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nvim_lsp",
|
||||||
|
"description": "Language Server Protocol integration for semantic queries and safe workspace-wide refactoring.",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rename", "definition", "references", "hover", "code_action"],
|
||||||
|
"description": "LSP action to perform"
|
||||||
|
},
|
||||||
|
"file": { "type": "string", "description": "Target file path" },
|
||||||
|
"line": { "type": "integer", "description": "1-indexed line number" },
|
||||||
|
"col": { "type": "integer", "description": "0-indexed column number" },
|
||||||
|
"new_name": { "type": "string", "description": "New name for rename action" }
|
||||||
|
},
|
||||||
|
"required": ["action"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nvim_ast",
|
||||||
|
"description": "Tree-sitter AST queries for semantic code exploration and outlining.",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["query", "outline"],
|
||||||
|
"description": "AST action to perform"
|
||||||
|
},
|
||||||
|
"file": { "type": "string", "description": "Target file path" },
|
||||||
|
"query": { "type": "string", "description": "Tree-sitter query string" },
|
||||||
|
"preset": { "type": "string", "description": "Query preset (e.g., 'functions', 'classes')" }
|
||||||
|
},
|
||||||
|
"required": ["action"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})),
|
})),
|
||||||
@@ -1456,6 +1494,28 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
execute_nvim_lua_with_args(code, vec![args_rmp])
|
execute_nvim_lua_with_args(code, vec![args_rmp])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
"undo" | "redo" => {
|
||||||
|
let is_undo = action == "undo";
|
||||||
|
let steps = args.get("steps").and_then(|v| v.as_i64()).unwrap_or(1);
|
||||||
|
let cmd = if is_undo {
|
||||||
|
format!("{}u", steps)
|
||||||
|
} else {
|
||||||
|
format!("{}<C-r>", steps)
|
||||||
|
};
|
||||||
|
let code = format!(
|
||||||
|
"
|
||||||
|
local pcall_ok, err = pcall(function()
|
||||||
|
vim.cmd('normal! {}')
|
||||||
|
end)
|
||||||
|
if not pcall_ok then
|
||||||
|
error('Failed to {}: ' .. tostring(err))
|
||||||
|
end
|
||||||
|
return '{} applied {} step(s)'
|
||||||
|
",
|
||||||
|
cmd, action, action, steps
|
||||||
|
);
|
||||||
|
handle_lua_result!(id, execute_nvim_lua(&code));
|
||||||
|
}
|
||||||
"open" => {
|
"open" => {
|
||||||
let args_rmp = json_to_rmpv(args);
|
let args_rmp = json_to_rmpv(args);
|
||||||
let code = "
|
let code = "
|
||||||
@@ -2100,6 +2160,229 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"nvim_lsp" => {
|
||||||
|
let args_rmp = json_to_rmpv(args);
|
||||||
|
let lua_code = match action {
|
||||||
|
"hover" => {
|
||||||
|
r#"
|
||||||
|
local args = ...
|
||||||
|
local bufnr = vim.fn.bufnr(args.file)
|
||||||
|
if bufnr == -1 then return "Buffer not found" end
|
||||||
|
local params = vim.lsp.util.make_position_params(0)
|
||||||
|
params.position.line = (tonumber(args.line) or 1) - 1
|
||||||
|
params.position.character = tonumber(args.col) or 0
|
||||||
|
params.textDocument.uri = vim.uri_from_bufnr(bufnr)
|
||||||
|
local res, err = vim.lsp.buf_request_sync(bufnr, 'textDocument/hover', params, 2000)
|
||||||
|
if err or not res then return "Error or timeout" end
|
||||||
|
for client_id, response in pairs(res) do
|
||||||
|
if response.result and response.result.contents then
|
||||||
|
local contents = response.result.contents
|
||||||
|
if type(contents) == 'table' and contents.value then return contents.value end
|
||||||
|
if type(contents) == 'string' then return contents end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return "No hover info"
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
"definition" => {
|
||||||
|
r#"
|
||||||
|
local args = ...
|
||||||
|
local bufnr = vim.fn.bufnr(args.file)
|
||||||
|
if bufnr == -1 then return "Buffer not found" end
|
||||||
|
local params = vim.lsp.util.make_position_params(0)
|
||||||
|
params.position.line = (tonumber(args.line) or 1) - 1
|
||||||
|
params.position.character = tonumber(args.col) or 0
|
||||||
|
params.textDocument.uri = vim.uri_from_bufnr(bufnr)
|
||||||
|
local res, err = vim.lsp.buf_request_sync(bufnr, 'textDocument/definition', params, 2000)
|
||||||
|
if err or not res then return "Error or timeout" end
|
||||||
|
local locations = {}
|
||||||
|
for client_id, response in pairs(res) do
|
||||||
|
if response.result then
|
||||||
|
local res_locs = vim.tbl_islist(response.result) and response.result or {response.result}
|
||||||
|
for _, loc in ipairs(res_locs) do
|
||||||
|
local uri = loc.uri or loc.targetUri
|
||||||
|
local range = loc.range or loc.targetSelectionRange
|
||||||
|
table.insert(locations, vim.uri_to_fname(uri) .. ":" .. (range.start.line + 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #locations == 0 then return "No definition found" end
|
||||||
|
return table.concat(locations, "\n")
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
"references" => {
|
||||||
|
r#"
|
||||||
|
local args = ...
|
||||||
|
local bufnr = vim.fn.bufnr(args.file)
|
||||||
|
if bufnr == -1 then return "Buffer not found" end
|
||||||
|
local params = vim.lsp.util.make_position_params(0)
|
||||||
|
params.position.line = (tonumber(args.line) or 1) - 1
|
||||||
|
params.position.character = tonumber(args.col) or 0
|
||||||
|
params.context = { includeDeclaration = true }
|
||||||
|
params.textDocument.uri = vim.uri_from_bufnr(bufnr)
|
||||||
|
local res, err = vim.lsp.buf_request_sync(bufnr, 'textDocument/references', params, 5000)
|
||||||
|
if err or not res then return "Error or timeout" end
|
||||||
|
local locations = {}
|
||||||
|
for client_id, response in pairs(res) do
|
||||||
|
if response.result then
|
||||||
|
for _, loc in ipairs(response.result) do
|
||||||
|
table.insert(locations, vim.uri_to_fname(loc.uri) .. ":" .. (loc.range.start.line + 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #locations == 0 then return "No references found" end
|
||||||
|
return table.concat(locations, "\n")
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
"rename" => {
|
||||||
|
r#"
|
||||||
|
local args = ...
|
||||||
|
local bufnr = vim.fn.bufnr(args.file)
|
||||||
|
if bufnr == -1 then return "Buffer not found" end
|
||||||
|
local new_name = args.new_name
|
||||||
|
if not new_name then return "Missing new_name" end
|
||||||
|
local params = vim.lsp.util.make_position_params(0)
|
||||||
|
params.position.line = (tonumber(args.line) or 1) - 1
|
||||||
|
params.position.character = tonumber(args.col) or 0
|
||||||
|
params.newName = new_name
|
||||||
|
params.textDocument.uri = vim.uri_from_bufnr(bufnr)
|
||||||
|
local res, err = vim.lsp.buf_request_sync(bufnr, 'textDocument/rename', params, 2000)
|
||||||
|
if err or not res then return "Error or timeout" end
|
||||||
|
local applied = false
|
||||||
|
for client_id, response in pairs(res) do
|
||||||
|
if response.result then
|
||||||
|
vim.lsp.util.apply_workspace_edit(response.result, vim.lsp.get_client_by_id(client_id).offset_encoding)
|
||||||
|
applied = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return applied and "Rename applied" or "No rename edits"
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
"code_action" => {
|
||||||
|
r#"
|
||||||
|
local args = ...
|
||||||
|
local bufnr = vim.fn.bufnr(args.file)
|
||||||
|
if bufnr == -1 then return "Buffer not found" end
|
||||||
|
local params = vim.lsp.util.make_range_params(0)
|
||||||
|
params.position = nil
|
||||||
|
params.range.start.line = (tonumber(args.line) or 1) - 1
|
||||||
|
params.range.start.character = tonumber(args.col) or 0
|
||||||
|
params.range['end'] = params.range.start
|
||||||
|
params.context = { diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr, params.range.start.line) }
|
||||||
|
params.textDocument.uri = vim.uri_from_bufnr(bufnr)
|
||||||
|
local res, err = vim.lsp.buf_request_sync(bufnr, 'textDocument/codeAction', params, 2000)
|
||||||
|
if err or not res then return "Error or timeout" end
|
||||||
|
|
||||||
|
local actions = {}
|
||||||
|
for client_id, response in pairs(res) do
|
||||||
|
if response.result then
|
||||||
|
for _, action in ipairs(response.result) do
|
||||||
|
if action.edit then
|
||||||
|
vim.lsp.util.apply_workspace_edit(action.edit, vim.lsp.get_client_by_id(client_id).offset_encoding)
|
||||||
|
table.insert(actions, "Applied: " .. (action.title or "unnamed"))
|
||||||
|
elseif action.command then
|
||||||
|
table.insert(actions, "Found command (not auto-applied): " .. (action.title or "unnamed"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #actions == 0 then return "No code actions available or applied" end
|
||||||
|
return table.concat(actions, "\n")
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
|
||||||
|
if lua_code.is_empty() {
|
||||||
|
send_error(
|
||||||
|
id,
|
||||||
|
-32602,
|
||||||
|
&format!("Invalid nvim_lsp action: {}", action),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
handle_lua_result!(
|
||||||
|
id,
|
||||||
|
execute_nvim_lua_with_args(lua_code, vec![args_rmp])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"nvim_ast" => {
|
||||||
|
let args_rmp = json_to_rmpv(args);
|
||||||
|
let lua_code = match action {
|
||||||
|
"query" => {
|
||||||
|
r#"
|
||||||
|
local args = ...
|
||||||
|
local bufnr = vim.fn.bufnr(args.file)
|
||||||
|
if bufnr == -1 then return "Buffer not found" end
|
||||||
|
local query_str = args.query
|
||||||
|
if not query_str then return "Missing query string" end
|
||||||
|
local parser = vim.treesitter.get_parser(bufnr)
|
||||||
|
if not parser then return "No TS parser for buffer" end
|
||||||
|
local tree = parser:parse()[1]
|
||||||
|
local root = tree:root()
|
||||||
|
local query = vim.treesitter.query.parse(parser:lang(), query_str)
|
||||||
|
local results = {}
|
||||||
|
for id, node, metadata in query:iter_captures(root, bufnr, 0, -1) do
|
||||||
|
local name = query.captures[id]
|
||||||
|
local row1, col1, row2, col2 = node:range()
|
||||||
|
local text = vim.treesitter.get_node_text(node, bufnr)
|
||||||
|
table.insert(results, name .. " at line " .. tostring(row1 + 1) .. ":\n" .. text)
|
||||||
|
end
|
||||||
|
return table.concat(results, "\n\n")
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
"outline" => {
|
||||||
|
r#"
|
||||||
|
local args = ...
|
||||||
|
local bufnr = vim.fn.bufnr(args.file)
|
||||||
|
if bufnr == -1 then return "Buffer not found" end
|
||||||
|
local parser = vim.treesitter.get_parser(bufnr)
|
||||||
|
if not parser then return "No TS parser for buffer" end
|
||||||
|
local tree = parser:parse()[1]
|
||||||
|
local root = tree:root()
|
||||||
|
local lang = parser:lang()
|
||||||
|
|
||||||
|
local q_str = ""
|
||||||
|
if lang == "rust" then
|
||||||
|
q_str = "(function_item name: (identifier) @name) (impl_item type: (type_identifier) @name) (struct_item name: (type_identifier) @name) (trait_item name: (type_identifier) @name)"
|
||||||
|
elseif lang == "python" then
|
||||||
|
q_str = "(function_definition name: (identifier) @name) (class_definition name: (identifier) @name)"
|
||||||
|
elseif lang == "javascript" or lang == "typescript" then
|
||||||
|
q_str = "(function_declaration name: (identifier) @name) (class_declaration name: (identifier) @name) (method_definition name: (property_identifier) @name)"
|
||||||
|
else
|
||||||
|
return "Outline preset not supported for language: " .. lang
|
||||||
|
end
|
||||||
|
|
||||||
|
local query = vim.treesitter.query.parse(lang, q_str)
|
||||||
|
local results = {}
|
||||||
|
for id, node, metadata in query:iter_captures(root, bufnr, 0, -1) do
|
||||||
|
local name_text = vim.treesitter.get_node_text(node, bufnr)
|
||||||
|
local parent = node:parent()
|
||||||
|
local row1, col1, row2, col2 = parent:range()
|
||||||
|
table.insert(results, name_text .. " (Line " .. tostring(row1 + 1) .. "-" .. tostring(row2 + 1) .. ")")
|
||||||
|
end
|
||||||
|
return table.concat(results, "\n")
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
|
||||||
|
if lua_code.is_empty() {
|
||||||
|
send_error(
|
||||||
|
id,
|
||||||
|
-32602,
|
||||||
|
&format!("Invalid nvim_ast action: {}", action),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
handle_lua_result!(
|
||||||
|
id,
|
||||||
|
execute_nvim_lua_with_args(lua_code, vec![args_rmp])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => send_error(id, -32601, "Method not found").await,
|
_ => send_error(id, -32601, "Method not found").await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2268,10 +2551,7 @@ mod tests {
|
|||||||
rmpv_to_json(&rmpv::Value::Integer(42.into())),
|
rmpv_to_json(&rmpv::Value::Integer(42.into())),
|
||||||
serde_json::json!(42)
|
serde_json::json!(42)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(rmpv_to_json(&rmpv::Value::F64(3.5)), serde_json::json!(3.5));
|
||||||
rmpv_to_json(&rmpv::Value::F64(3.14)),
|
|
||||||
serde_json::json!(3.14)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
rmpv_to_json(&rmpv::Value::String("hello".into())),
|
rmpv_to_json(&rmpv::Value::String("hello".into())),
|
||||||
serde_json::json!("hello")
|
serde_json::json!("hello")
|
||||||
|
|||||||
+11
-14
@@ -295,20 +295,17 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(|act_val| {
|
.filter_map(|act_val| {
|
||||||
if act_val["category"] == "TASK_EVENT" {
|
if act_val["category"] == "TASK_EVENT" {
|
||||||
if let Some(details_str) = act_val["details"].as_str() {
|
let details_str = act_val["details"].as_str()?;
|
||||||
if let Ok(event_val) =
|
let event_val =
|
||||||
serde_json::from_str::<serde_json::Value>(details_str)
|
serde_json::from_str::<serde_json::Value>(details_str).ok()?;
|
||||||
{
|
return Some(
|
||||||
return Some(
|
serde_json::json!({
|
||||||
serde_json::json!({
|
"jsonrpc": "2.0",
|
||||||
"jsonrpc": "2.0",
|
"method": "notifications/task/completed",
|
||||||
"method": "notifications/task/completed",
|
"params": event_val
|
||||||
"params": event_val
|
})
|
||||||
})
|
.to_string(),
|
||||||
.to_string(),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -193,14 +193,14 @@ fn fallback_embed(text: &str) -> Vec<f32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum EmbeddingModel {
|
enum EmbeddingModel {
|
||||||
Candle(CandleEmbeddingModel),
|
Candle(Box<CandleEmbeddingModel>),
|
||||||
Fallback,
|
Fallback,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmbeddingModel {
|
impl EmbeddingModel {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
match CandleEmbeddingModel::new() {
|
match CandleEmbeddingModel::new() {
|
||||||
Ok(model) => EmbeddingModel::Candle(model),
|
Ok(model) => EmbeddingModel::Candle(Box::new(model)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"Failed to initialize Candle BERT model ({e}); falling back to deterministic offline embeddings."
|
"Failed to initialize Candle BERT model ({e}); falling back to deterministic offline embeddings."
|
||||||
|
|||||||
+5
-4
@@ -808,7 +808,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cli_parsing_default() {
|
fn test_cli_parsing_default() {
|
||||||
let cli = Cli::try_parse_from(&["mcp-memory-server"]).unwrap();
|
let cli = Cli::try_parse_from(["mcp-memory-server"]).unwrap();
|
||||||
assert!(cli.command.is_none());
|
assert!(cli.command.is_none());
|
||||||
assert!(!cli.exit);
|
assert!(!cli.exit);
|
||||||
assert!(!cli.restart);
|
assert!(!cli.restart);
|
||||||
@@ -816,7 +816,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cli_parsing_exit_and_target() {
|
fn test_cli_parsing_exit_and_target() {
|
||||||
let cli = Cli::try_parse_from(&[
|
let cli = Cli::try_parse_from([
|
||||||
"mcp-memory-server",
|
"mcp-memory-server",
|
||||||
"--exit",
|
"--exit",
|
||||||
"--target",
|
"--target",
|
||||||
@@ -829,7 +829,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cli_parsing_gate_set() {
|
fn test_cli_parsing_gate_set() {
|
||||||
let cli = Cli::try_parse_from(&[
|
let cli = Cli::try_parse_from([
|
||||||
"mcp-memory-server",
|
"mcp-memory-server",
|
||||||
"gate",
|
"gate",
|
||||||
"set",
|
"set",
|
||||||
@@ -865,7 +865,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cli_parsing_gate_verify() {
|
fn test_cli_parsing_gate_verify() {
|
||||||
let cli = Cli::try_parse_from(&[
|
let cli = Cli::try_parse_from([
|
||||||
"mcp-memory-server",
|
"mcp-memory-server",
|
||||||
"gate",
|
"gate",
|
||||||
"verify",
|
"verify",
|
||||||
@@ -930,6 +930,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
#[allow(clippy::await_holding_lock)]
|
||||||
async fn test_run_server_graceful_shutdown() {
|
async fn test_run_server_graceful_shutdown() {
|
||||||
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
@@ -1398,7 +1398,7 @@ mod tests {
|
|||||||
let handler = MemoryHandler::new(state);
|
let handler = MemoryHandler::new(state);
|
||||||
|
|
||||||
// 1. Verify invalid LLM requests fail gracefully with isError: true or structured response
|
// 1. Verify invalid LLM requests fail gracefully with isError: true or structured response
|
||||||
for (tool_name, _tool) in &handler.tools {
|
for tool_name in handler.tools.keys() {
|
||||||
let invalid_req = json!({
|
let invalid_req = json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 200,
|
"id": 200,
|
||||||
|
|||||||
+1
-2
@@ -1,5 +1,6 @@
|
|||||||
use crate::models::*;
|
use crate::models::*;
|
||||||
use crate::search::MemoryIndex;
|
use crate::search::MemoryIndex;
|
||||||
|
pub use crate::search::{SearchResult as UnifiedSearchResult, SearchService};
|
||||||
use crate::store::Store;
|
use crate::store::Store;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -575,5 +576,3 @@ mod tests {
|
|||||||
assert!(activities_updated[0].timestamp >= activities_updated[1].timestamp);
|
assert!(activities_updated[0].timestamp >= activities_updated[1].timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::search::{SearchResult as UnifiedSearchResult, SearchService};
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use image::DynamicImage;
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_img() {
|
fn test_img() {
|
||||||
let mut img = image::DynamicImage::new_rgb8(10, 10);
|
let mut img = image::DynamicImage::new_rgb8(10, 10);
|
||||||
img.invert();
|
img.invert();
|
||||||
let img2 = img.adjust_contrast(20.0);
|
let _img2 = img.adjust_contrast(20.0);
|
||||||
}
|
}
|
||||||
@@ -101,9 +101,10 @@ fn test_dashboard_route_parity() {
|
|||||||
let mut registered_routes = HashSet::new();
|
let mut registered_routes = HashSet::new();
|
||||||
for part in setup_content.split(".route(") {
|
for part in setup_content.split(".route(") {
|
||||||
let trimmed = part.trim_start();
|
let trimmed = part.trim_start();
|
||||||
if trimmed.starts_with('"') {
|
#[allow(clippy::collapsible_if)]
|
||||||
if let Some(end_quote) = trimmed[1..].find('"') {
|
if let Some(stripped) = trimmed.strip_prefix('"') {
|
||||||
let route = &trimmed[1..=end_quote];
|
if let Some(end_quote) = stripped.find('"') {
|
||||||
|
let route = &stripped[..end_quote];
|
||||||
registered_routes.insert(route.to_string());
|
registered_routes.insert(route.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,8 +120,10 @@ fn test_dashboard_route_parity() {
|
|||||||
let rest = &dashboard_content[start..];
|
let rest = &dashboard_content[start..];
|
||||||
let quote_char = dashboard_content[..start].chars().last();
|
let quote_char = dashboard_content[..start].chars().last();
|
||||||
let mut endpoint = String::new();
|
let mut endpoint = String::new();
|
||||||
|
#[allow(clippy::collapsible_if)]
|
||||||
if let Some(q) = quote_char {
|
if let Some(q) = quote_char {
|
||||||
if q == '`' || q == '"' || q == '\'' {
|
let is_quote = q == '`' || q == '"' || q == '\'';
|
||||||
|
if is_quote {
|
||||||
if let Some(close_idx) = rest.find(q) {
|
if let Some(close_idx) = rest.find(q) {
|
||||||
let full_call = &rest[..close_idx];
|
let full_call = &rest[..close_idx];
|
||||||
let clean = if let Some(q_idx) = full_call.find('?') {
|
let clean = if let Some(q_idx) = full_call.find('?') {
|
||||||
|
|||||||
Reference in new issue
Block a user