Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,11 @@
<recordList owner="MorphologicalData" property="Phonemes"/>
</clerk>
<clerk id="naturalClasses">
<recordList owner="MorphologicalData" property="NaturalClasses"/>
<dynamicloaderinfo assemblyPath="MorphologyEditorDll.dll"
class="SIL.FieldWorks.XWorks.MorphologyEditor.NaturalClassRecordClerk"/>
<recordList owner="MorphologicalData" property="NaturalClasses">
<dynamicloaderinfo assemblyPath="MorphologyEditorDll.dll" class="SIL.FieldWorks.XWorks.MorphologyEditor.NaturalClassRecordList"/>
</recordList>
</clerk>
<clerk id="environments">
<recordList owner="MorphologicalData" property="Environments"/>
Expand Down
4 changes: 3 additions & 1 deletion Src/LexText/Morphology/AffixRuleFormulaControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,9 @@ public override void SetMappingNaturalClass(SelectionHelper sel = null)
var natClasses = new HashSet<ICmObject>();
foreach (var nc in m_cache.LangProject.PhonologicalDataOA.NaturalClassesOS)
{
if (nc.ClassID == PhNCFeaturesTags.kClassId)
// Rule mappings only allow feature-based, user-defined natural classes
// to be selected.
if (nc is IPhNCFeatures featNC && IsFeatureBasedNCUserDefined(featNC))
natClasses.Add(nc);
}
var selectedNc = DisplayChooser(MEStrings.ksRuleNCOpt, MEStrings.ksRuleNCChooserLink,
Expand Down
6 changes: 5 additions & 1 deletion Src/LexText/Morphology/AffixRuleFormulaSlice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ public bool OnMappingSetNaturalClass(object args)
public bool OnDisplayMappingJumpToNaturalClass(object commandObject, ref XCore.UIItemDisplayProperties display)
{
CheckDisposed();
bool enable = AffixRuleFormulaControl.IsNCIndexCurrent;
// Note that a mapping's Natural Class is always feature-based.
// For feature-based NCs, we only want to allow jumping to the NC
// if it is user-defined.
var mapping = AffixRuleFormulaControl.CurrentObject as IMoModifyFromInput;
bool enable = mapping != null && RuleFormulaControl.IsFeatureBasedNCUserDefined(mapping.ModificationRA);
display.Enabled = enable;
display.Visible = enable;
return true;
Expand Down
23 changes: 17 additions & 6 deletions Src/LexText/Morphology/MEStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Src/LexText/Morphology/MEStrings.resx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Expand Down Expand Up @@ -239,6 +239,9 @@
<data name="ksRuleNCFeatsName" xml:space="preserve">
<value>Created automatically for rule "{0}"</value>
</data>
<data name="ksRuleNCSharedNameFormat" xml:space="preserve">
<value>Shared: {0}</value>
</data>
<data name="ksRuleRedoInsert" xml:space="preserve">
<value>Redo Insert {0}</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) 2026 SIL International
// This software is licensed under the LGPL, version 2.1 or later
// (http://www.gnu.org/licenses/lgpl-2.1.html)

using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using SIL.LCModel;

namespace SIL.FieldWorks.XWorks.MorphologyEditor
{
/// <summary>
/// Tests to confirm that <see cref="NaturalClassRecordList"/> includes every
/// natural class except a feature-based one generated automatically for a rule.
/// </summary>
[TestFixture]
public class NaturalClassRecordListTests : RuleFormulaVcTestBase
{
private IEnumerable<int> NCRecordList(params ICmObject[] natClasses)
{
return NaturalClassRecordList.ExcludeAutoGenerated(
natClasses.Select(nc => nc.Hvo), Cache);
}
[Test]
public void AutoGeneratedClass_IsExcluded()
{
IPhNCFeatures natClass = AddAutoGeneratedFeatureNaturalClass("s to n",
new Dictionary<string, string> { { "vd", "+" } });

// Since the only NC is autogenerated, the NC record list should be empty
Assert.That(NCRecordList(natClass), Is.Empty);
}

[Test]
public void NamedFeatureClass_IsIncluded()
{
IPhNCFeatures natClass = AddFeatureNaturalClass("Voiced", "Vd",
new Dictionary<string, string> { { "vd", "+" } });

Assert.That(NCRecordList(natClass), Is.EqualTo(new[] { natClass.Hvo }));
}

/// <summary>
/// A feature-based class the user creates starts with no name, so an unnamed class must
/// be included for the user to be able to name it.
/// </summary>
[Test]
public void UnnamedFeatureClass_IsIncluded()
{
IPhNCFeatures natClass = AddFeatureNaturalClass(null, null,
new Dictionary<string, string> { { "vd", "+" } });

Assert.That(NCRecordList(natClass), Is.EqualTo(new[] { natClass.Hvo }));
}

[Test]
public void SegmentClass_IsIncluded()
{
IPhNCSegments natClass = AddSegmentNaturalClass("Vowels", "V");

Assert.That(NCRecordList(natClass), Is.EqualTo(new[] { natClass.Hvo }));
}

/// <summary>
/// A class given an abbreviation by the user is considered user defined,
/// so it should display in the NC list.
/// </summary>
[Test]
public void AbbreviatedGeneratedClass_IsIncluded()
{
IPhNCFeatures natClass = AddAutoGeneratedFeatureNaturalClass("s to n",
new Dictionary<string, string> { { "vd", "+" } });
natClass.Abbreviation.SetAnalysisDefaultWritingSystem("Vd");

Assert.That(NCRecordList(natClass), Is.EqualTo(new[] { natClass.Hvo }));
}

/// <summary>
/// A class with the "shared" prefix is not considered user defined if it is used
/// by at most one rule, so it should not display in the NC list.
/// </summary>
[Test]
public void PrefixedClassUsedByOneRule_IsExcluded()
{
IPhNCFeatures natClass = AddAutoGeneratedFeatureNaturalClass("s to n",
new Dictionary<string, string> { { "vd", "+" } });
natClass.Name.SetUserWritingSystem(string.Format(MEStrings.ksRuleNCSharedNameFormat,
natClass.Name.UserDefaultWritingSystem.Text));
AddStrucDescNCContext(AddRegularRule("rule A"), natClass);

Assert.That(NCRecordList(natClass), Is.Empty);
}

/// <summary>
/// A class used by more than one rule is considered user defined,
/// so it should display in the NC list.
/// </summary>
[Test]
public void ClassSharedByTwoRules_IsIncluded()
{
IPhNCFeatures natClass = AddAutoGeneratedFeatureNaturalClass("s to n",
new Dictionary<string, string> { { "vd", "+" } });
AddStrucDescNCContext(AddRegularRule("rule A"), natClass);
AddStrucDescNCContext(AddRegularRule("rule B"), natClass);

Assert.That(NCRecordList(natClass), Is.EqualTo(new[] { natClass.Hvo }));
}
}
}
Loading
Loading