165 lines
6.1 KiB
C#
165 lines
6.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Threading;
|
|
using AxCopilot.SDK;
|
|
using AxCopilot.Services;
|
|
|
|
namespace AxCopilot.Handlers;
|
|
|
|
public class ScaffoldHandler : IActionHandler
|
|
{
|
|
internal record ScaffoldTemplate([property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("description")] string Description, [property: JsonPropertyName("paths")] string[] Paths);
|
|
|
|
private static readonly string TemplateDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "templates");
|
|
|
|
private static readonly ScaffoldTemplate[] BuiltInTemplates = new ScaffoldTemplate[5]
|
|
{
|
|
new ScaffoldTemplate("webapi", "Web API 프로젝트", new string[8] { "src/Controllers/", "src/Models/", "src/Services/", "src/Middleware/", "tests/", "docs/", "README.md", ".gitignore" }),
|
|
new ScaffoldTemplate("console", "콘솔 애플리케이션", new string[5] { "src/", "src/Core/", "src/Services/", "tests/", "README.md" }),
|
|
new ScaffoldTemplate("wpf", "WPF 데스크톱 앱", new string[8] { "src/Views/", "src/ViewModels/", "src/Models/", "src/Services/", "src/Themes/", "src/Assets/", "tests/", "docs/" }),
|
|
new ScaffoldTemplate("data", "데이터 파이프라인", new string[9] { "src/Extractors/", "src/Transformers/", "src/Loaders/", "config/", "scripts/", "tests/", "data/input/", "data/output/", "README.md" }),
|
|
new ScaffoldTemplate("docs", "문서 프로젝트", new string[5] { "docs/", "images/", "templates/", "README.md", "CHANGELOG.md" })
|
|
};
|
|
|
|
public string? Prefix => "scaffold";
|
|
|
|
public PluginMetadata Metadata => new PluginMetadata("Scaffold", "프로젝트 스캐폴딩 — scaffold", "1.0", "AX");
|
|
|
|
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
|
{
|
|
string q = query.Trim();
|
|
IEnumerable<ScaffoldTemplate> second = LoadUserTemplates();
|
|
List<ScaffoldTemplate> list = BuiltInTemplates.Concat(second).ToList();
|
|
if (string.IsNullOrWhiteSpace(q))
|
|
{
|
|
List<LauncherItem> list2 = list.Select((ScaffoldTemplate t) => new LauncherItem("[" + t.Name + "] " + t.Description, $"{t.Paths.Length}개 폴더/파일 · Enter → 대상 경로 입력 후 생성", null, t, null, "\ue8b7")).ToList();
|
|
list2.Insert(0, new LauncherItem("프로젝트 스캐폴딩", $"총 {list.Count}개 템플릿 · 이름을 입력해 필터링", null, null, null, "\ue946"));
|
|
return Task.FromResult((IEnumerable<LauncherItem>)list2);
|
|
}
|
|
if (q.Contains('\\') || q.Contains('/'))
|
|
{
|
|
int num = q.LastIndexOf(' ');
|
|
if (num > 0)
|
|
{
|
|
string text = q.Substring(0, num).Trim();
|
|
string text2 = q;
|
|
int num2 = num + 1;
|
|
string templateName = text2.Substring(num2, text2.Length - num2).Trim();
|
|
ScaffoldTemplate scaffoldTemplate = list.FirstOrDefault((ScaffoldTemplate t) => t.Name.Equals(templateName, StringComparison.OrdinalIgnoreCase));
|
|
if (scaffoldTemplate != null)
|
|
{
|
|
return Task.FromResult((IEnumerable<LauncherItem>)new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("[" + scaffoldTemplate.Name + "] → " + text, $"{scaffoldTemplate.Paths.Length}개 폴더/파일 생성 · Enter로 실행", null, ValueTuple.Create(text, scaffoldTemplate), null, "\ue74e")));
|
|
}
|
|
}
|
|
}
|
|
IEnumerable<ScaffoldTemplate> source = list.Where((ScaffoldTemplate t) => t.Name.Contains(q, StringComparison.OrdinalIgnoreCase) || t.Description.Contains(q, StringComparison.OrdinalIgnoreCase));
|
|
List<LauncherItem> list3 = source.Select(delegate(ScaffoldTemplate t)
|
|
{
|
|
string text3 = string.Join(", ", t.Paths.Take(4));
|
|
if (t.Paths.Length > 4)
|
|
{
|
|
text3 += $" ... (+{t.Paths.Length - 4})";
|
|
}
|
|
return new LauncherItem("[" + t.Name + "] " + t.Description, text3 + " · 사용법: scaffold [대상경로] " + t.Name, null, t, null, "\ue8b7");
|
|
}).ToList();
|
|
if (!list3.Any())
|
|
{
|
|
list3.Add(new LauncherItem("'" + q + "'에 해당하는 템플릿 없음", "scaffold 으로 전체 목록 확인", null, null, null, "\ue7ba"));
|
|
}
|
|
return Task.FromResult((IEnumerable<LauncherItem>)list3);
|
|
}
|
|
|
|
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
|
|
{
|
|
if (!(item.Data is (string, ScaffoldTemplate) tuple))
|
|
{
|
|
if (item.Data is ScaffoldTemplate scaffoldTemplate)
|
|
{
|
|
string usage = "scaffold [대상경로] " + scaffoldTemplate.Name;
|
|
try
|
|
{
|
|
Application current = Application.Current;
|
|
if (current != null)
|
|
{
|
|
((DispatcherObject)current).Dispatcher.Invoke((Action)delegate
|
|
{
|
|
Clipboard.SetText(usage);
|
|
});
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
var (basePath, template) = tuple;
|
|
return CreateStructure(basePath, template);
|
|
}
|
|
|
|
private static Task CreateStructure(string basePath, ScaffoldTemplate template)
|
|
{
|
|
try
|
|
{
|
|
int num = 0;
|
|
string[] paths = template.Paths;
|
|
foreach (string text in paths)
|
|
{
|
|
string path = Path.Combine(basePath, text.Replace('/', Path.DirectorySeparatorChar));
|
|
if (text.EndsWith('/') || text.EndsWith('\\') || !Path.HasExtension(text))
|
|
{
|
|
Directory.CreateDirectory(path);
|
|
}
|
|
else
|
|
{
|
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
|
if (!File.Exists(path))
|
|
{
|
|
File.WriteAllText(path, "");
|
|
}
|
|
}
|
|
num++;
|
|
}
|
|
NotificationService.Notify("스캐폴딩 완료", $"[{template.Name}] {num}개 항목 생성 → {basePath}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogService.Error("스캐폴딩 실패: " + ex.Message);
|
|
NotificationService.Notify("AX Copilot", "스캐폴딩 실패: " + ex.Message);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private static IEnumerable<ScaffoldTemplate> LoadUserTemplates()
|
|
{
|
|
if (!Directory.Exists(TemplateDir))
|
|
{
|
|
yield break;
|
|
}
|
|
string[] files = Directory.GetFiles(TemplateDir, "*.json");
|
|
foreach (string file in files)
|
|
{
|
|
ScaffoldTemplate tmpl = null;
|
|
try
|
|
{
|
|
string json = File.ReadAllText(file);
|
|
tmpl = JsonSerializer.Deserialize<ScaffoldTemplate>(json);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
if (tmpl != null)
|
|
{
|
|
yield return tmpl;
|
|
}
|
|
}
|
|
}
|
|
}
|