Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions RimeSharp.PowerShell/Cmdlets/CandidateCmdlets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
using System.Management.Automation;

namespace RimeSharp.PowerShell.Cmdlets;

/// <summary>
/// Enumerate candidates by their global index.
/// </summary>
[Cmdlet(VerbsCommon.Get, "RimeCandidate")]
[OutputType(typeof(RimeCandidateInfo))]
public sealed class GetRimeCandidateCmdlet : PSCmdlet, IDisposable
{
private Rime? _rime;

[Parameter(Position = 0)]
[ValidateRange(0, int.MaxValue)]
public int Start { get; set; }

[Parameter(Position = 1)]
[ValidateRange(0, int.MaxValue)]
public int Count { get; set; } = int.MaxValue;

[Parameter(
Position = 2,
ValueFromPipeline = true
)]
public RimeSession? Session { get; set; }

protected override void BeginProcessing()
{
_rime = Rime.Instance();
Session ??= SessionState.PSVariable.GetValue("global:RimeDefaultSession") as RimeSession;
}

protected override void ProcessRecord()
{
if (_rime is null) return;
if (Session is null)
{
var ex = new InvalidOperationException(
"No RIME session specified. Pipe a session from Start-Rime or use -Session.");
ThrowTerminatingError(new ErrorRecord(ex, "RimeSessionMissing",
ErrorCategory.InvalidOperation, null));
return;
}

SessionValidation.EnsureSessionValid(this, _rime, Session);
var candidates = _rime.GetCandidates(Session.Id, Start, Count);
for (var i = 0; i < candidates.Length; ++i)
{
WriteObject(new RimeCandidateInfo(Start + i, candidates[i]));
}
}

protected override void StopProcessing() => Dispose();
public void Dispose() => _rime = null;
}

/// <summary>
/// Select a candidate by its global or current-page index.
/// </summary>
[Cmdlet(VerbsCommon.Select, "RimeCandidate")]
[OutputType(typeof(void))]
public sealed class SelectRimeCandidateCmdlet : PSCmdlet, IDisposable
{
private Rime? _rime;

[Parameter(
Position = 0,
Mandatory = true
)]
[ValidateRange(0, int.MaxValue)]
public int Index { get; set; }

[Parameter(
Position = 1,
ValueFromPipeline = true
)]
public RimeSession? Session { get; set; }

[Parameter]
public SwitchParameter OnCurrentPage { get; set; }

protected override void BeginProcessing()
{
_rime = Rime.Instance();
Session ??= SessionState.PSVariable.GetValue("global:RimeDefaultSession") as RimeSession;
}

protected override void ProcessRecord()
{
if (_rime is null) return;
if (Session is null)
{
var ex = new InvalidOperationException(
"No RIME session specified. Pipe a session from Start-Rime or use -Session.");
ThrowTerminatingError(new ErrorRecord(ex, "RimeSessionMissing",
ErrorCategory.InvalidOperation, null));
return;
}

SessionValidation.EnsureSessionValid(this, _rime, Session);
if (!_rime.SelectCandidate(Session.Id, Index, OnCurrentPage))
{
var ex = new ArgumentException(
$"Candidate index {Index} could not be selected.", nameof(Index));
ThrowTerminatingError(new ErrorRecord(ex, "RimeCandidateSelectionFailed",
ErrorCategory.InvalidArgument, Index));
}
}

protected override void StopProcessing() => Dispose();
public void Dispose() => _rime = null;
}

/// <summary>
/// Remove a candidate by its global or current-page index.
/// </summary>
[Cmdlet(VerbsCommon.Remove, "RimeCandidate", SupportsShouldProcess = true)]
[OutputType(typeof(void))]
public sealed class RemoveRimeCandidateCmdlet : PSCmdlet, IDisposable
{
private Rime? _rime;

[Parameter(
Position = 0,
Mandatory = true
)]
[ValidateRange(0, int.MaxValue)]
public int Index { get; set; }

[Parameter(
Position = 1,
ValueFromPipeline = true
)]
public RimeSession? Session { get; set; }

[Parameter]
public SwitchParameter OnCurrentPage { get; set; }

protected override void BeginProcessing()
{
_rime = Rime.Instance();
Session ??= SessionState.PSVariable.GetValue("global:RimeDefaultSession") as RimeSession;
}

protected override void ProcessRecord()
{
if (_rime is null) return;
if (Session is null)
{
var ex = new InvalidOperationException(
"No RIME session specified. Pipe a session from Start-Rime or use -Session.");
ThrowTerminatingError(new ErrorRecord(ex, "RimeSessionMissing",
ErrorCategory.InvalidOperation, null));
return;
}

SessionValidation.EnsureSessionValid(this, _rime, Session);
if (!ShouldProcess($"candidate index {Index}", "Remove RIME candidate"))
{
return;
}

if (!_rime.DeleteCandidate(Session.Id, Index, OnCurrentPage))
{
var ex = new ArgumentException(
$"Candidate index {Index} could not be removed.", nameof(Index));
ThrowTerminatingError(new ErrorRecord(ex, "RimeCandidateRemovalFailed",
ErrorCategory.InvalidArgument, Index));
}
}

protected override void StopProcessing() => Dispose();
public void Dispose() => _rime = null;
}

/// <summary>
/// Highlight a candidate by its global or current-page index.
/// </summary>
[Cmdlet(VerbsLifecycle.Invoke, "RimeHighlight")]
[OutputType(typeof(void))]
public sealed class InvokeRimeHighlightCmdlet : PSCmdlet, IDisposable
{
private Rime? _rime;

[Parameter(
Position = 0,
Mandatory = true
)]
[ValidateRange(0, int.MaxValue)]
public int Index { get; set; }

[Parameter(
Position = 1,
ValueFromPipeline = true
)]
public RimeSession? Session { get; set; }

[Parameter]
public SwitchParameter OnCurrentPage { get; set; }

protected override void BeginProcessing()
{
_rime = Rime.Instance();
Session ??= SessionState.PSVariable.GetValue("global:RimeDefaultSession") as RimeSession;
}

protected override void ProcessRecord()
{
if (_rime is null) return;
if (Session is null)
{
var ex = new InvalidOperationException(
"No RIME session specified. Pipe a session from Start-Rime or use -Session.");
ThrowTerminatingError(new ErrorRecord(ex, "RimeSessionMissing",
ErrorCategory.InvalidOperation, null));
return;
}

SessionValidation.EnsureSessionValid(this, _rime, Session);
if (!_rime.HighlightCandidate(Session.Id, Index, OnCurrentPage))
{
var ex = new ArgumentException(
$"Candidate index {Index} could not be highlighted.", nameof(Index));
ThrowTerminatingError(new ErrorRecord(ex, "RimeCandidateHighlightFailed",
ErrorCategory.InvalidArgument, Index));
}
}

protected override void StopProcessing() => Dispose();
public void Dispose() => _rime = null;
}
135 changes: 135 additions & 0 deletions RimeSharp.PowerShell/Cmdlets/KeyCmdlets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System.Management.Automation;

namespace RimeSharp.PowerShell.Cmdlets;

/// <summary>
/// Simulate a key sequence (e.g. "nihao" or "Down") and return a
/// <see cref="RimeResponse"/> containing commit, status, and context.
/// Internally calls <c>SimulateKeySequence</c>.
/// </summary>
[Cmdlet(VerbsCommunications.Send, "RimeKey")]
[OutputType(typeof(RimeResponse))]
public sealed class SendRimeKeyCmdlet : PSCmdlet, IDisposable
{
private Rime? _rime;

[Parameter(
Position = 1,
ValueFromPipeline = true
)]
public RimeSession? Session { get; set; }

[Parameter(
Position = 0,
Mandatory = true
)]
public string Sequence { get; set; } = "";

protected override void BeginProcessing()
{
_rime = Rime.Instance();
Session ??= SessionState.PSVariable.GetValue("global:RimeDefaultSession") as RimeSession;
}

protected override void ProcessRecord()
{
if (_rime is null) return;
if (Session is null)
{
var ex = new InvalidOperationException(
"No RIME session specified. Pipe a session from Start-Rime or use -Session.");
ThrowTerminatingError(new ErrorRecord(ex, "RimeSessionMissing",
ErrorCategory.InvalidOperation, null));
return;
}

SessionValidation.EnsureSessionValid(this, _rime, Session);
if (!_rime.SimulateKeySequence(Session.Id, Sequence))
{
var ex = new ArgumentException(
$"Key sequence '{Sequence}' could not be simulated.", nameof(Sequence));
ThrowTerminatingError(new ErrorRecord(ex, "RimeKeySequenceFailed",
ErrorCategory.InvalidArgument, Sequence));
return;
}

using var commit = _rime.GetCommit(Session.Id);
using var status = _rime.GetStatus(Session.Id);
using var context = _rime.GetContext(Session.Id);

var response = new RimeResponse(
commit.Text,
new RimeStatusSnapshot(status),
new RimeContextSnapshot(context));
WriteObject(response);
}

protected override void StopProcessing()
{
Dispose();
}

public void Dispose()
{
_rime = null;
}
}

/// <summary>
/// Send a single raw key event (key code + modifier mask) to a RIME session.
/// Uses <c>ProcessKey</c> for low-level key handling.
/// </summary>
[Cmdlet(VerbsCommunications.Send, "RimeKeyEvent")]
[OutputType(typeof(bool))]
public sealed class SendRimeKeyEventCmdlet : PSCmdlet, IDisposable
{
private Rime? _rime;

[Parameter(
Position = 2,
ValueFromPipeline = true
)]
public RimeSession? Session { get; set; }

[Parameter(
Position = 0,
Mandatory = true
)]
public int KeyCode { get; set; }

[Parameter(Position = 1)]
public int Mask { get; set; }

protected override void BeginProcessing()
{
_rime = Rime.Instance();
Session ??= SessionState.PSVariable.GetValue("global:RimeDefaultSession") as RimeSession;
}

protected override void ProcessRecord()
{
if (_rime is null) return;
if (Session is null)
{
var ex = new InvalidOperationException(
"No RIME session specified. Pipe a session from Start-Rime or use -Session.");
ThrowTerminatingError(new ErrorRecord(ex, "RimeSessionMissing",
ErrorCategory.InvalidOperation, null));
return;
}

SessionValidation.EnsureSessionValid(this, _rime, Session);
var handled = _rime.ProcessKey(Session.Id, KeyCode, Mask);
WriteObject(handled);
}

protected override void StopProcessing()
{
Dispose();
}

public void Dispose()
{
_rime = null;
}
}
Loading
Loading