Skip to content

Commit

Permalink
Merge pull request #13 from huangxiuqi/cmd-history
Browse files Browse the repository at this point in the history
feat:添加命令历史记录
  • Loading branch information
zkhssb authored Jan 29, 2024
2 parents 5f33821 + f8b0347 commit e7349a9
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 6 deletions.
1 change: 1 addition & 0 deletions NectarRCON/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public partial class App
services.AddSingleton<ILanguageService, LanguageService>();
services.AddSingleton<IConfigService, ConfigService>();
services.AddSingleton<ILogService, LogService>();
services.AddSingleton<IHistoryService, HistoryService>();
services.AddSingleton<IServerPasswordService, ServerPasswordService>();

services.AddSingleton<IThemeService, ThemeService>();
Expand Down
33 changes: 33 additions & 0 deletions NectarRCON/Interfaces/IHistoryService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace NectarRCON.Interfaces;

public class HistoryNode
{
public string? Cmd { get; set; }

public HistoryNode? Prev { get; set; }

public HistoryNode? Next { get; set; }
}

public interface IHistoryService
{
/// <summary>
/// 获取前一条命令
/// </summary>
/// <param name="current"></param>
/// <returns></returns>
HistoryNode? Prev(HistoryNode? current);

/// <summary>
/// 获取后一条命令
/// </summary>
/// <param name="current"></param>
/// <returns></returns>
HistoryNode? Next(HistoryNode? current);

/// <summary>
/// 输入命令
/// </summary>
/// <param name="cmd"></param>
HistoryNode? InputCmd(string cmd);
}
155 changes: 155 additions & 0 deletions NectarRCON/Services/HistoryService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using NectarRCON.Interfaces;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace NectarRCON.Services;

public class HistoryService : IHistoryService
{

/// <summary>
/// 双向链表保存历史记录
/// </summary>
private HistoryNode? _head;
private HistoryNode? _tail;

private readonly Dictionary<string, HistoryNode> _map;

/// <summary>
/// 限制记录条数
/// </summary>
private int _limit = 20;

public HistoryService()
{
_map = [];
if (!Path.Exists("./logs"))
{
Directory.CreateDirectory("./logs");
}

var stream = File.Open($"./logs/history", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
using StreamReader reader = new StreamReader(stream);
string? line;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (!string.IsNullOrEmpty(line))
{
AppendNode(line);
}
}
}

public HistoryNode? InputCmd(string cmd)
{
var node = AppendNode(cmd);
Save();
return node;
}

private HistoryNode? AppendNode(string cmd)
{
cmd = cmd.Trim();
HistoryNode node;
if (_map.ContainsKey(cmd))
{
// 已存在的记录,将其移动到链表尾部
node = _map[cmd];
if (node == _tail)
{
return null;
}
else if (node == _head)
{
_head = _head.Next;
}

if (node.Prev != null)
{
node.Prev.Next = node.Next;
}
if (node.Next != null)
{
node.Next.Prev = node.Prev;
}
node.Prev = null;
node.Next = null;
}
else
{
node = new HistoryNode()
{
Cmd = cmd,
};
_map.Add(cmd, node);
}

if (_head == null || _tail == null)
{
_head = _tail = node;
}
else
{
_tail.Next = node;
node.Prev = _tail;
_tail = node;
}

// 超出限制,移除链表头部记录
if (_map.Count() > _limit)
{
var head = _head;
_head = _head.Next;
if (_head != null)
{
_head.Prev = null;
}
if (!string.IsNullOrEmpty(head.Cmd))
{
_map.Remove(head.Cmd);
}
}

return null;
}

public HistoryNode? Next(HistoryNode? current)
{
if (current == null)
{
return null;
}
return current.Next;
}

public HistoryNode? Prev(HistoryNode? current)
{
if (current == null)
{
return _tail;
}
else if (current.Prev == null)
{
return current;
}
return current.Prev;
}

private void Save()
{
using var stream = File.Open($"./logs/history", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
stream.Seek(0, SeekOrigin.Begin);
HistoryNode? node = _head;
while (node != null) {
if (!string.IsNullOrEmpty(node.Cmd))
{
string cmd = node.Cmd + "\n";
stream.Write(Encoding.UTF8.GetBytes(cmd));
}
node = node.Next;
}
}
}
31 changes: 26 additions & 5 deletions NectarRCON/ViewModels/MainPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public partial class MainPageViewModel : ObservableObject
{
private static readonly RconSettingsDp RconSettings = DpFile.LoadSingleton<RconSettingsDp>();
private readonly ILogService _logService;
private readonly IHistoryService _historyService;
private readonly IServerPasswordService _serverPasswordService;
private IRconConnection _rconConnectService;
private readonly INavigationService _navigationService;
Expand All @@ -34,6 +35,7 @@ public partial class MainPageViewModel : ObservableObject

private MainPage? _page;
private TextBox? _logTextBox;
private HistoryNode? _historyNode;

[ObservableProperty] private string _commandText = string.Empty;
[ObservableProperty] private string _logText = string.Empty;
Expand All @@ -43,6 +45,7 @@ public partial class MainPageViewModel : ObservableObject
public MainPageViewModel()
{
_logService = App.GetService<ILogService>();
_historyService = App.GetService<IHistoryService>();
_serverPasswordService = App.GetService<IServerPasswordService>();
_navigationService = App.GetService<INavigationService>();
_languageService = App.GetService<ILanguageService>();
Expand Down Expand Up @@ -87,7 +90,7 @@ private async void Load(RoutedEventArgs e)
// GetLogs
LogText = string.Empty;
LogText = _logService.GetText();

_page = e.Source as MainPage;
await ConnectAsync();
}
Expand All @@ -105,7 +108,7 @@ private async void ReConnect()
private async Task ConnectAsync()
{
Log.Information($"[ConnectAsync] 准备连接到服务器");

IsMultipleConnection = _rconConnectionInfoService.HasMultipleInformation;
_rconConnectService.OnConnected -= OnConnected;
_rconConnectService.OnMessage -= OnMessage;
Expand All @@ -123,7 +126,7 @@ private async Task ConnectAsync()
{
IsLoaded = true,
});

Log.Information($"[ConnectAsync] 连接服务: {_rconConnectService.GetType().FullName}, 是否为多连接: {IsMultipleConnection}");

_logTextBox = (TextBox)LogicalTreeHelper.FindLogicalNode(_page, "LogText");
Expand Down Expand Up @@ -151,7 +154,7 @@ private async Task ConnectAsync()
catch (Exception ex)
{
Log.Error($"[ConnectAsync] 连接遇到错误: {ex}");

var msg = _languageService.GetKey("text.server.connect.fail.text")
.Replace("\\n", "\n")
.Replace("%s", ex.Message);
Expand Down Expand Up @@ -234,10 +237,28 @@ private void Run()
private void KeyDown(KeyEventArgs e)
{
var textBox = (System.Windows.Controls.TextBox)e.Source;
_commandText = textBox.Text;
if (e.Key == Key.Enter)
{
var text = textBox.Text.Trim();
if (string.IsNullOrEmpty(text))
{
return;
}
_commandText = text;
_historyNode = _historyService.InputCmd(_commandText);
Run();
}
else if (e.Key == Key.Up)
{
_historyNode = _historyService.Prev(_historyNode);
textBox.Text = _historyNode?.Cmd;
textBox.Select(textBox.Text?.Length ?? 0, 0);
}
else if (e.Key == Key.Down)
{
_historyNode = _historyService.Next(_historyNode);
textBox.Text = _historyNode?.Cmd;
textBox.Select(textBox.Text?.Length ?? 0, 0);
}
}
}
2 changes: 1 addition & 1 deletion NectarRCON/Views/Pages/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
PlaceholderText="/">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="KeyDown">
EventName="PreviewKeyDown">
<i:InvokeCommandAction
PassEventArgsToCommand="True"
Command="{Binding KeyDownCommand}"/>
Expand Down

0 comments on commit e7349a9

Please sign in to comment.