Initial commit to new repository

This commit is contained in:
2026-04-03 18:22:19 +09:00
commit 4458bb0f52
7672 changed files with 452440 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
using System.IO;
using System.Text.Json;
namespace AxCopilot.Services;
/// <summary>
/// 런처 항목 실행 횟수를 추적하여 퍼지 검색 결과 정렬에 활용합니다.
/// 저장 위치: %APPDATA%\AxCopilot\usage.json
/// 형식: { "키": 횟수 } — 키는 IndexEntry.Path (파일/폴더/앱 경로)
/// </summary>
internal static class UsageRankingService
{
private static readonly string _dataFile = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"AxCopilot", "usage.json");
private static Dictionary<string, int> _counts = new(StringComparer.OrdinalIgnoreCase);
private static bool _loaded = false;
private static readonly object _lock = new();
/// <summary>
/// 항목 실행 시 호출하여 카운트를 증가시킵니다.
/// </summary>
public static void RecordExecution(string key)
{
if (string.IsNullOrWhiteSpace(key)) return;
EnsureLoaded();
lock (_lock)
{
_counts.TryGetValue(key, out var current);
_counts[key] = current + 1;
}
_ = SaveAsync();
}
/// <summary>
/// 주어진 키의 실행 횟수를 반환합니다. 없으면 0.
/// </summary>
public static int GetScore(string key)
{
if (string.IsNullOrWhiteSpace(key)) return 0;
EnsureLoaded();
lock (_lock)
{
return _counts.TryGetValue(key, out var count) ? count : 0;
}
}
/// <summary>
/// 실행 횟수 기준으로 내림차순 정렬하는 컴파러를 반환합니다.
/// 동점이면 원래 순서 유지 (stable sort).
/// </summary>
public static IEnumerable<T> SortByUsage<T>(IEnumerable<T> items, Func<T, string?> keySelector)
{
EnsureLoaded();
return items
.Select((item, idx) => (item, idx, score: GetScore(keySelector(item) ?? "")))
.OrderByDescending(x => x.score)
.ThenBy(x => x.idx)
.Select(x => x.item);
}
// ─── 내부 ──────────────────────────────────────────────────────────────────
private static void EnsureLoaded()
{
if (_loaded) return;
lock (_lock)
{
if (_loaded) return;
try
{
if (File.Exists(_dataFile))
{
var json = File.ReadAllText(_dataFile);
var data = JsonSerializer.Deserialize<Dictionary<string, int>>(json);
if (data != null)
_counts = new Dictionary<string, int>(data, StringComparer.OrdinalIgnoreCase);
}
}
catch (Exception ex)
{
LogService.Warn($"usage.json 로드 실패: {ex.Message}");
}
_loaded = true;
}
}
private static async Task SaveAsync()
{
try
{
Dictionary<string, int> snapshot;
lock (_lock) { snapshot = new Dictionary<string, int>(_counts); }
Directory.CreateDirectory(Path.GetDirectoryName(_dataFile)!);
var json = JsonSerializer.Serialize(snapshot, new JsonSerializerOptions { WriteIndented = false });
await File.WriteAllTextAsync(_dataFile, json);
}
catch (Exception ex)
{
LogService.Warn($"usage.json 저장 실패: {ex.Message}");
}
}
}