Skip to content

Commit

Permalink
Fix \a on strings (#1454)
Browse files Browse the repository at this point in the history
* Fix `\a` on strings

* Add a test
  • Loading branch information
wixoaGit authored Sep 11, 2023
1 parent 8841689 commit 5244699
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 38 deletions.
39 changes: 39 additions & 0 deletions Content.Tests/DMProject/Tests/Text/StringInterpolation9.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/datum/thing
var/name = "thing"

/datum/Thing
var/name = "Thing"

/datum/proper_thing
var/name = "\proper thing"

/datum/plural_things
var/name = "things"
var/gender = PLURAL

/proc/RunTest()
// Lowercase \a on datums
ASSERT("\a [new /datum/thing]" == "a thing")
ASSERT("\a [new /datum/Thing]" == "Thing")
ASSERT("\a [new /datum/proper_thing]" == "thing")
ASSERT("\a [new /datum/plural_things]" == "some things")

// Uppercase \A on datums
ASSERT("\A [new /datum/thing]" == "A thing")
ASSERT("\A [new /datum/Thing]" == "Thing")
ASSERT("\A [new /datum/proper_thing]" == "thing")
ASSERT("\A [new /datum/plural_things]" == "Some things")

// Lowercase \a on strings
ASSERT("\a ["thing"]" == "a thing")
ASSERT("\a ["Thing"]" == "Thing")
ASSERT("\a ["\proper thing"]" == "thing")

// Uppercase \A on strings
ASSERT("\A ["thing"]" == "A thing")
ASSERT("\A ["Thing"]" == "Thing")
ASSERT("\A ["\proper thing"]" == "thing")

// Invalid \a
ASSERT("\a [123]" == "")
ASSERT("\A [123]" == "")
19 changes: 12 additions & 7 deletions OpenDreamRuntime/Objects/DreamObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,6 @@ public static bool StringIsProper(string str) {
return true;
case StringFormatEncoder.FormatSuffix.Improper:
return false;
default:
break;
}
}

Expand Down Expand Up @@ -312,9 +310,7 @@ public string GetDisplayName(StringFormatEncoder.FormatSuffix? suffix = null) {
if (this is DreamObjectClient client)
return client.Connection.Session!.Name;

if (!TryGetVariable("name", out DreamValue nameVar) || !nameVar.TryGetValueAsString(out string? name))
return ObjectDefinition.Type.ToString();

var name = GetRawName();
bool isProper = StringIsProper(name);
name = StringFormatEncoder.RemoveFormatting(name); // TODO: Care about other formatting macros for obj names beyond \proper & \improper
if(!isProper) {
Expand All @@ -335,9 +331,18 @@ public string GetDisplayName(StringFormatEncoder.FormatSuffix? suffix = null) {
/// Similar to <see cref="GetDisplayName"/> except it just returns the name as plaintext, with formatting removed. No article or anything.
/// </summary>
public string GetNameUnformatted() {
return StringFormatEncoder.RemoveFormatting(GetRawName());
}

/// <summary>
/// Returns the name of this object with no formatting evaluated
/// </summary>
/// <returns></returns>
public string GetRawName() {
if (!TryGetVariable("name", out DreamValue nameVar) || !nameVar.TryGetValueAsString(out string? name))
return ObjectDefinition?.Type.ToString() ?? String.Empty;
return StringFormatEncoder.RemoveFormatting(name);
return ObjectDefinition.Type.ToString();

return name;
}
#endregion Name Helpers

Expand Down
71 changes: 40 additions & 31 deletions OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -372,26 +372,28 @@ public static ProcStatus FormatString(DMProcState state) {
nextInterpIndex++;
continue;
}
case StringFormatEncoder.FormatSuffix.StringifyNoArticle:
{
if (interps[nextInterpIndex].TryGetValueAsDreamObject(out var dreamObject) && dreamObject != null) {
case StringFormatEncoder.FormatSuffix.StringifyNoArticle: {
if (interps[nextInterpIndex].TryGetValueAsDreamObject<DreamObject>(out var dreamObject)) {
formattedString.Append(dreamObject.GetNameUnformatted());
} else if (interps[nextInterpIndex].TryGetValueAsString(out var interpStr)) {
formattedString.Append(StringFormatEncoder.RemoveFormatting(interpStr));
}

// NOTE probably should put this above the TryGetAsDreamObject function and continue if formatting has occured
if(postPrefix != null) { // Cursed Hack
switch(postPrefix) {
switch (postPrefix) {
case StringFormatEncoder.FormatSuffix.LowerRoman:
ToRoman(ref formattedString, interps, nextInterpIndex, false);
break;
case StringFormatEncoder.FormatSuffix.UpperRoman:
ToRoman(ref formattedString, interps, nextInterpIndex, true);
break;
default: break;
}

postPrefix = null;
}
//Things that aren't objects just print nothing in this case

//Things that aren't objects or strings just print nothing in this case
prevInterpIndex = nextInterpIndex;
nextInterpIndex++;
continue;
Expand All @@ -414,33 +416,40 @@ public static ProcStatus FormatString(DMProcState state) {
continue;
}
case StringFormatEncoder.FormatSuffix.UpperIndefiniteArticle:
case StringFormatEncoder.FormatSuffix.LowerIndefiniteArticle:
{
bool wasCapital = formatType == StringFormatEncoder.FormatSuffix.UpperIndefiniteArticle; // saves some wordiness with the ternaries below
if (interps[nextInterpIndex].TryGetValueAsDreamObject(out var dreamObject) && dreamObject != null)
{
bool hasName = dreamObject.TryGetVariable("name", out var objectName);
string nameStr = objectName.Stringify();
if (!hasName) continue; // datums that lack a name var don't use articles
if (DreamObject.StringIsProper(nameStr)) continue; // Proper nouns don't need articles, I guess.

if (dreamObject.TryGetVariable("gender", out var gender)) // Aayy babe whats ya pronouns
{
if (gender.TryGetValueAsString(out var str) && str == "plural") // NOTE: In Byond, this part does not work if var/gender is not a native property of this object.
{
formattedString.Append(wasCapital ? "Some" : "some");
continue;
}
}
if (DreamObject.StringStartsWithVowel(nameStr))
{
formattedString.Append(wasCapital ? "An " : "an ");
continue;
case StringFormatEncoder.FormatSuffix.LowerIndefiniteArticle: {
var interpValue = interps[nextInterpIndex];
string displayName;
bool isPlural = false;

if (interpValue.TryGetValueAsDreamObject<DreamObject>(out var dreamObject)) {
displayName = dreamObject.GetRawName();

// Aayy babe whats ya pronouns
if (dreamObject.TryGetVariable("gender", out var gender) &&
gender.TryGetValueAsString(out var genderStr)) {
// NOTE: In Byond, this part does not work if var/gender is not a native property of this object.
isPlural = (genderStr == "plural");
}
formattedString.Append(wasCapital ? "A " : "a ");
continue;
} else if (interpValue.TryGetValueAsString(out var interpStr)) {
displayName = interpStr;
} else {
break;
}
continue;

if (DreamObject.StringIsProper(displayName))
break; // Proper nouns don't need articles, I guess.

// saves some wordiness with the ternaries below
bool wasCapital = formatType == StringFormatEncoder.FormatSuffix.UpperIndefiniteArticle;

if (isPlural)
formattedString.Append(wasCapital ? "Some " : "some ");
else if (DreamObject.StringStartsWithVowel(displayName))
formattedString.Append(wasCapital ? "An " : "an ");
else
formattedString.Append(wasCapital ? "A " : "a ");

break;
}
//Suffix macros
case StringFormatEncoder.FormatSuffix.UpperSubjectPronoun:
Expand Down

0 comments on commit 5244699

Please sign in to comment.