211 lines
8.3 KiB
C#
211 lines
8.3 KiB
C#
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows;
|
|
using AxCopilot.SDK;
|
|
using AxCopilot.Services;
|
|
using AxCopilot.Themes;
|
|
|
|
namespace AxCopilot.Handlers;
|
|
|
|
/// <summary>
|
|
/// 텍스트 일괄 처리 핸들러. "batch" 프리픽스로 사용합니다.
|
|
/// 클립보드 텍스트의 각 줄에 동시에 변환을 적용합니다.
|
|
/// 예: batch prefix [텍스트] → 각 줄 앞에 [텍스트] 추가
|
|
/// batch suffix [텍스트] → 각 줄 뒤에 [텍스트] 추가
|
|
/// batch number → 줄번호 추가
|
|
/// batch sort → 줄 정렬
|
|
/// batch unique → 중복 줄 제거
|
|
/// batch wrap " → 각 줄을 "로 감싸기
|
|
/// batch replace A B → A를 B로 치환
|
|
/// batch csv → 줄 → CSV 한 줄로 합치기
|
|
/// batch split , → CSV 한 줄 → 여러 줄로 분리
|
|
/// Enter → 결과를 클립보드에 복사.
|
|
/// </summary>
|
|
public class BatchTextHandler : IActionHandler
|
|
{
|
|
public string? Prefix => "batch";
|
|
|
|
public PluginMetadata Metadata => new(
|
|
"BatchText",
|
|
"텍스트 일괄 처리 — batch",
|
|
"1.0",
|
|
"AX");
|
|
|
|
private static readonly (string Cmd, string Desc)[] Commands =
|
|
[
|
|
("prefix [텍스트]", "각 줄 앞에 텍스트 추가"),
|
|
("suffix [텍스트]", "각 줄 뒤에 텍스트 추가"),
|
|
("wrap [문자]", "각 줄을 지정 문자로 감싸기 (예: wrap \")"),
|
|
("number", "줄번호 추가 (1. 2. 3. ...)"),
|
|
("sort", "줄 오름차순 정렬"),
|
|
("sortd", "줄 내림차순 정렬"),
|
|
("reverse", "줄 순서 뒤집기"),
|
|
("unique", "중복 줄 제거"),
|
|
("trim", "각 줄 앞뒤 공백 제거"),
|
|
("replace [A] [B]", "A를 B로 전체 치환"),
|
|
("csv", "줄들을 쉼표로 합쳐 한 줄로"),
|
|
("split [구분자]", "한 줄을 구분자로 분리하여 여러 줄로"),
|
|
("indent [N]", "각 줄 앞에 공백 N개 추가"),
|
|
("unindent", "각 줄의 선행 공백/탭 제거"),
|
|
];
|
|
|
|
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
|
{
|
|
var q = query.Trim();
|
|
|
|
if (string.IsNullOrWhiteSpace(q))
|
|
{
|
|
var items = Commands.Select(c => new LauncherItem(
|
|
$"batch {c.Cmd}",
|
|
c.Desc,
|
|
null, null,
|
|
Symbol: Symbols.Text)).ToList<LauncherItem>();
|
|
|
|
items.Insert(0, new LauncherItem(
|
|
"텍스트 일괄 처리",
|
|
"클립보드 텍스트의 각 줄에 변환 적용 · 명령 입력",
|
|
null, null, Symbol: Symbols.Info));
|
|
|
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
|
}
|
|
|
|
// 클립보드 읽기
|
|
string? text = null;
|
|
try
|
|
{
|
|
if (Application.Current?.Dispatcher.Invoke(() => Clipboard.ContainsText()) == true)
|
|
text = Application.Current.Dispatcher.Invoke(() => Clipboard.GetText());
|
|
}
|
|
catch (Exception) { }
|
|
|
|
if (string.IsNullOrEmpty(text))
|
|
{
|
|
return Task.FromResult<IEnumerable<LauncherItem>>(
|
|
[
|
|
new LauncherItem("클립보드에 텍스트가 없습니다", "텍스트를 복사한 후 시도하세요",
|
|
null, null, Symbol: Symbols.Warning)
|
|
]);
|
|
}
|
|
|
|
var lines = text.Split('\n').Select(l => l.TrimEnd('\r')).ToArray();
|
|
var parts = q.Split(' ', 2, StringSplitOptions.TrimEntries);
|
|
var cmd = parts[0].ToLowerInvariant();
|
|
var arg = parts.Length > 1 ? parts[1] : "";
|
|
|
|
string? result = null;
|
|
string? desc = null;
|
|
|
|
try
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case "prefix":
|
|
result = string.Join("\n", lines.Select(l => arg + l));
|
|
desc = $"각 줄 앞에 '{arg}' 추가";
|
|
break;
|
|
case "suffix":
|
|
result = string.Join("\n", lines.Select(l => l + arg));
|
|
desc = $"각 줄 뒤에 '{arg}' 추가";
|
|
break;
|
|
case "wrap":
|
|
var w = string.IsNullOrEmpty(arg) ? "\"" : arg;
|
|
result = string.Join("\n", lines.Select(l => w + l + w));
|
|
desc = $"각 줄을 '{w}'로 감싸기";
|
|
break;
|
|
case "number":
|
|
result = string.Join("\n", lines.Select((l, i) => $"{i + 1}. {l}"));
|
|
desc = "줄번호 추가";
|
|
break;
|
|
case "sort":
|
|
result = string.Join("\n", lines.Order());
|
|
desc = "오름차순 정렬";
|
|
break;
|
|
case "sortd":
|
|
result = string.Join("\n", lines.OrderDescending());
|
|
desc = "내림차순 정렬";
|
|
break;
|
|
case "reverse":
|
|
result = string.Join("\n", lines.Reverse());
|
|
desc = "줄 순서 뒤집기";
|
|
break;
|
|
case "unique":
|
|
var unique = lines.Distinct().ToArray();
|
|
result = string.Join("\n", unique);
|
|
desc = $"중복 제거: {lines.Length}줄 → {unique.Length}줄";
|
|
break;
|
|
case "trim":
|
|
result = string.Join("\n", lines.Select(l => l.Trim()));
|
|
desc = "각 줄 공백 제거";
|
|
break;
|
|
case "replace":
|
|
var rParts = arg.Split(' ', 2, StringSplitOptions.TrimEntries);
|
|
if (rParts.Length == 2)
|
|
{
|
|
result = text.Replace(rParts[0], rParts[1]);
|
|
desc = $"'{rParts[0]}' → '{rParts[1]}' 치환";
|
|
}
|
|
break;
|
|
case "csv":
|
|
result = string.Join(",", lines.Select(l => l.Trim()));
|
|
desc = $"{lines.Length}줄 → CSV 한 줄";
|
|
break;
|
|
case "split":
|
|
var sep = string.IsNullOrEmpty(arg) ? "," : arg;
|
|
result = string.Join("\n", text.Split(sep));
|
|
desc = $"'{sep}' 기준 분리";
|
|
break;
|
|
case "indent":
|
|
var n = int.TryParse(arg, out var indent) ? indent : 4;
|
|
var pad = new string(' ', n);
|
|
result = string.Join("\n", lines.Select(l => pad + l));
|
|
desc = $"{n}칸 들여쓰기";
|
|
break;
|
|
case "unindent":
|
|
result = string.Join("\n", lines.Select(l => l.TrimStart(' ', '\t')));
|
|
desc = "선행 공백 제거";
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Task.FromResult<IEnumerable<LauncherItem>>(
|
|
[
|
|
new LauncherItem($"처리 오류: {ex.Message}", "입력 데이터를 확인하세요",
|
|
null, null, Symbol: Symbols.Error)
|
|
]);
|
|
}
|
|
|
|
if (result == null)
|
|
{
|
|
return Task.FromResult<IEnumerable<LauncherItem>>(
|
|
[
|
|
new LauncherItem($"알 수 없는 명령: {cmd}", "batch 만 입력하면 전체 명령 목록",
|
|
null, null, Symbol: Symbols.Warning)
|
|
]);
|
|
}
|
|
|
|
var preview = result.Length > 120 ? result[..117] + "…" : result;
|
|
preview = preview.Replace("\n", "↵ ");
|
|
|
|
return Task.FromResult<IEnumerable<LauncherItem>>(
|
|
[
|
|
new LauncherItem(
|
|
$"[{desc}] → Enter로 클립보드 복사",
|
|
preview,
|
|
null, result,
|
|
Symbol: Symbols.Text)
|
|
]);
|
|
}
|
|
|
|
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
|
|
{
|
|
if (item.Data is string text)
|
|
{
|
|
try { Application.Current?.Dispatcher.Invoke(() => Clipboard.SetText(text)); }
|
|
catch (Exception) { }
|
|
NotificationService.Notify("일괄 처리 완료", "변환 결과가 클립보드에 복사되었습니다");
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|