Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
11 changes: 8 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ insert_final_newline = true
trim_trailing_whitespace = true

# Markdown files
[*.{md}]
[*.md]
end_of_line = crlf
trim_trailing_whitespace = false

Expand All @@ -39,7 +39,6 @@ indent_size = 2
# Json files
[*.json]
end_of_line = crlf
indent_size = 4

# Linux scripts
[*.sh]
Expand Down Expand Up @@ -73,7 +72,7 @@ csharp_prefer_simple_using_statement = true
csharp_prefer_static_anonymous_function = true
csharp_prefer_static_local_function = true
csharp_prefer_system_threading_lock = true
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
csharp_preferred_modifier_order = public,private,protected,internal,file,static,abstract,sealed,virtual,override,readonly,unsafe,volatile,async,extern,new,partial:warning
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
csharp_space_after_cast = false
Expand Down Expand Up @@ -204,3 +203,9 @@ dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
dotnet_style_readonly_field = true
dotnet_style_require_accessibility_modifiers = for_non_interface_members

# ReSharper settings
resharper_csharp_trailing_comma_in_multiline_lists = true
resharper_csharp_var_for_built_in_types = false
resharper_csharp_var_when_type_is_apparent = false
resharper_csharp_var_when_type_is_not_apparent = false
28 changes: 23 additions & 5 deletions CODESTYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,20 +168,38 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
- C#, XML, YAML, JSON, Windows scripts: CRLF
- Linux scripts (`.sh`): LF

6. **`#region`**: Do not use. Prefer logical file/folder/namespace organization.
7. **Member ordering (StyleCop-like)**: Constants → fields → constructors → properties → indexers → methods → events → operators → finalizers → delegates → nested types
6. **`#region`**: Do not use regions. Prefer logical file/folder/namespace organization.
7. **Member ordering (StyleCop SA1201)**: conststatic readonly → static fields → instance readonly fields → instance fields → constructors → public (events → properties → indexers → methods → operators)non-public in same order → nested types

### Comments and Documentation

1. **XML documentation**
- `<GenerateDocumentationFile>true</GenerateDocumentationFile>`
- Missing XML comments for public APIs are suppressed (`.editorconfig`)
- Single-line summaries
- Must document all public surfaces.
- Single-line summaries, additional details in remarks, document input parameters, returns values, exceptions, and add crefs

```csharp
/// <summary>
/// This property always returns a value < 1.
/// Example of a single line summary.
/// </summary>
/// <remarks>
/// Additional important details about usage.
/// Multiple lines if needed.
/// </remarks>
/// <param name="category">
/// The quote category to request
/// </param>
/// <param name="cancellationToken">
/// A <see cref="System.Threading.CancellationToken"/> that can be used to cancel the request.
/// </param>
/// <returns>
/// A <see cref="string"/> containing the quote text.
/// </returns>
/// <exception cref="System.ArgumentException">
/// Thrown when <paramref name="category"/> is not a supported value.
/// </exception>
public async Task<string> GetQuoteOfTheDayAsync(string category, CancellationToken cancellationToken) {}
```

2. **Code analysis suppressions**
Expand Down Expand Up @@ -248,7 +266,7 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to

### Testing Conventions

1. **Framework**: xUnit with FluentAssertions
1. **Framework**: xUnit with AwesomeAssertions

```csharp
[Fact]
Expand Down
3 changes: 3 additions & 0 deletions LanguageTags.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
"ANTLR",
"arevela",
"boont",
"chamí",
"CLDR",
"codegen",
"csdevkit",
"datebadge",
"davidanson",
"derbend",
"dotnettools",
"Emberá",
"extlang",
"finalizers",
"gruntfuggly",
Expand Down Expand Up @@ -52,6 +54,7 @@
"pyfisch",
"Qaaa",
"Qabx",
"reparsed",
"rspeer",
"Serilog",
"Simoncic",
Expand Down
4 changes: 2 additions & 2 deletions LanguageTags/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal bool LogAndPropagate(
[CallerMemberName] string function = "unknown"
)
{
LogCatchException(logger, function, exception);
logger.LogCatchException(function, exception);
return false;
}

Expand All @@ -20,7 +20,7 @@ internal bool LogAndHandle(
[CallerMemberName] string function = "unknown"
)
{
LogCatchException(logger, function, exception);
logger.LogCatchException(function, exception);
return true;
}
}
Expand Down
36 changes: 24 additions & 12 deletions LanguageTags/Iso6392Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,10 @@ await JsonSerializer
.ConfigureAwait(false);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Reliability",
"CA2007:Consider calling ConfigureAwait on the awaited task",
Justification = "https://github.com/dotnet/roslyn-analyzers/issues/7185"
)]
internal static async Task GenCodeAsync(string fileName, Iso6392Data iso6392)
{
ArgumentNullException.ThrowIfNull(iso6392);
StreamWriter writer = new(
using StreamWriter writer = new(
new FileStream(
fileName,
FileMode.Create,
Expand All @@ -208,18 +203,25 @@ internal static async Task GenCodeAsync(string fileName, Iso6392Data iso6392)
{
NewLine = "\r\n",
};
await using ConfiguredAsyncDisposable writerScope = writer.ConfigureAwait(false);

ConfiguredTaskAwaitable WriteLineAsync(string value) =>
writer.WriteLineAsync(value).ConfigureAwait(false);

await WriteLineAsync("namespace ptr727.LanguageTags;");
await WriteLineAsync(string.Empty);
await WriteLineAsync("/// <summary>");
await WriteLineAsync("/// Provides access to ISO 639-2 language code data.");
await WriteLineAsync("/// </summary>");
await WriteLineAsync(
$"[System.CodeDom.Compiler.GeneratedCode(\"{typeof(Iso6392Data).FullName}\", \"1.0\")]"
);
await WriteLineAsync("public sealed partial class Iso6392Data");
await WriteLineAsync("{");
await WriteLineAsync(" /// <summary>");
await WriteLineAsync(
" /// Creates an instance loaded from the embedded ISO 639-2 dataset."
);
await WriteLineAsync(" /// </summary>");
await WriteLineAsync(
" /// <returns>The populated <see cref=\"Iso6392Data\"/> instance.</returns>"
);
await WriteLineAsync(" public static Iso6392Data Create() =>");
await WriteLineAsync(" new()");
await WriteLineAsync(" {");
Expand Down Expand Up @@ -248,6 +250,10 @@ await WriteLineAsync(
await WriteLineAsync(" ],");
await WriteLineAsync(" };");
await WriteLineAsync("}");
return;

ConfiguredTaskAwaitable WriteLineAsync(string value) =>
writer.WriteLineAsync(value).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -258,19 +264,25 @@ await WriteLineAsync(
/// <summary>
/// Finds an ISO 639-2 language record by language code or description.
/// </summary>
/// <remarks>
/// Matching is case-insensitive and checks Part 2/B, Part 2/T, Part 1, then (optionally) reference name.
/// </remarks>
/// <param name="languageTag">The language code or description to search for.</param>
/// <param name="includeDescription">If true, searches in the reference name field; otherwise, only searches language codes.</param>
/// <returns>The matching <see cref="Iso6392Record"/> or null if not found.</returns>
/// <returns>The first matching <see cref="Iso6392Record"/>, or null when no match is found.</returns>
public Iso6392Record? Find(string? languageTag, bool includeDescription) =>
Find(languageTag, includeDescription, LogOptions.CreateLogger<Iso6392Data>());

/// <summary>
/// Finds an ISO 639-2 language record by language code or description using the specified options.
/// </summary>
/// <remarks>
/// Matching is case-insensitive and checks Part 2/B, Part 2/T, Part 1, then (optionally) reference name.
/// </remarks>
/// <param name="languageTag">The language code or description to search for.</param>
/// <param name="includeDescription">If true, searches in the reference name field; otherwise, only searches language codes.</param>
/// <param name="options">The options used to configure logging.</param>
/// <returns>The matching <see cref="Iso6392Record"/> or null if not found.</returns>
/// <returns>The first matching <see cref="Iso6392Record"/>, or null when no match is found.</returns>
public Iso6392Record? Find(string? languageTag, bool includeDescription, Options? options) =>
Find(languageTag, includeDescription, LogOptions.CreateLogger<Iso6392Data>(options));

Expand Down
5 changes: 5 additions & 0 deletions LanguageTags/Iso6392DataGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ namespace ptr727.LanguageTags;
/// <summary>
/// Provides access to ISO 639-2 language code data.
/// </summary>
[System.CodeDom.Compiler.GeneratedCode("ptr727.LanguageTags.Iso6392Data", "1.0")]
public sealed partial class Iso6392Data
{
/// <summary>
/// Creates an instance loaded from the embedded ISO 639-2 dataset.
/// </summary>
/// <returns>The populated <see cref="Iso6392Data"/> instance.</returns>
public static Iso6392Data Create() =>
new()
{
Expand Down
32 changes: 22 additions & 10 deletions LanguageTags/Iso6393Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,11 @@ await JsonSerializer
.ConfigureAwait(false);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Reliability",
"CA2007:Consider calling ConfigureAwait on the awaited task",
Justification = "https://github.com/dotnet/roslyn-analyzers/issues/7185"
)]
internal static async Task GenCodeAsync(string fileName, Iso6393Data iso6393)
{
ArgumentNullException.ThrowIfNull(iso6393);

StreamWriter writer = new(
using StreamWriter writer = new(
new FileStream(
fileName,
FileMode.Create,
Expand All @@ -235,18 +230,25 @@ internal static async Task GenCodeAsync(string fileName, Iso6393Data iso6393)
{
NewLine = "\r\n",
};
await using ConfiguredAsyncDisposable writerScope = writer.ConfigureAwait(false);

ConfiguredTaskAwaitable WriteLineAsync(string value) =>
writer.WriteLineAsync(value).ConfigureAwait(false);

await WriteLineAsync("namespace ptr727.LanguageTags;");
await WriteLineAsync(string.Empty);
await WriteLineAsync("/// <summary>");
await WriteLineAsync("/// Provides access to ISO 639-3 language code data.");
await WriteLineAsync("/// </summary>");
await WriteLineAsync(
$"[System.CodeDom.Compiler.GeneratedCode(\"{typeof(Iso6393Data).FullName}\", \"1.0\")]"
);
await WriteLineAsync("public sealed partial class Iso6393Data");
await WriteLineAsync("{");
await WriteLineAsync(" /// <summary>");
await WriteLineAsync(
" /// Creates an instance loaded from the embedded ISO 639-3 dataset."
);
await WriteLineAsync(" /// </summary>");
await WriteLineAsync(
" /// <returns>The populated <see cref=\"Iso6393Data\"/> instance.</returns>"
);
await WriteLineAsync(" public static Iso6393Data Create() =>");
await WriteLineAsync(" new()");
await WriteLineAsync(" {");
Expand Down Expand Up @@ -284,6 +286,10 @@ await WriteLineAsync(
await WriteLineAsync(" ],");
await WriteLineAsync(" };");
await WriteLineAsync("}");
return;

ConfiguredTaskAwaitable WriteLineAsync(string value) =>
writer.WriteLineAsync(value).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -294,6 +300,9 @@ await WriteLineAsync(
/// <summary>
/// Finds an ISO 639-3 language record by language code or description.
/// </summary>
/// <remarks>
/// Matching is case-insensitive and checks Id, Part 2/B, Part 2/T, Part 1, then (optionally) reference name.
/// </remarks>
/// <param name="languageTag">The language code or description to search for.</param>
/// <param name="includeDescription">If true, searches in the reference name field; otherwise, only searches language codes.</param>
/// <returns>The matching <see cref="Iso6393Record"/> or null if not found.</returns>
Expand All @@ -303,6 +312,9 @@ await WriteLineAsync(
/// <summary>
/// Finds an ISO 639-3 language record by language code or description using the specified options.
/// </summary>
/// <remarks>
/// Matching is case-insensitive and checks Id, Part 2/B, Part 2/T, Part 1, then (optionally) reference name.
/// </remarks>
/// <param name="languageTag">The language code or description to search for.</param>
/// <param name="includeDescription">If true, searches in the reference name field; otherwise, only searches language codes.</param>
/// <param name="options">The options used to configure logging.</param>
Expand Down
5 changes: 5 additions & 0 deletions LanguageTags/Iso6393DataGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ namespace ptr727.LanguageTags;
/// <summary>
/// Provides access to ISO 639-3 language code data.
/// </summary>
[System.CodeDom.Compiler.GeneratedCode("ptr727.LanguageTags.Iso6393Data", "1.0")]
public sealed partial class Iso6393Data
{
/// <summary>
/// Creates an instance loaded from the embedded ISO 639-3 dataset.
/// </summary>
/// <returns>The populated <see cref="Iso6393Data"/> instance.</returns>
public static Iso6393Data Create() =>
new()
{
Expand Down
8 changes: 7 additions & 1 deletion LanguageTags/LanguageLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public string GetIetfFromIso(string languageTag)
/// Converts an IETF BCP 47 language tag to its ISO equivalent.
/// </summary>
/// <param name="languageTag">The IETF BCP 47 language tag to convert.</param>
/// <returns>The ISO language code, or "und" if the conversion fails.</returns>
/// <returns>The ISO 639-2/B language code, or "und" if the conversion fails.</returns>
public string GetIsoFromIetf(string languageTag)
{
// Undetermined
Expand Down Expand Up @@ -260,12 +260,18 @@ public bool IsMatch(string prefix, string languageTag)
/// <summary>
/// Determines if two language tags are equivalent (case-insensitive).
/// </summary>
/// <param name="tag1">The first language tag.</param>
/// <param name="tag2">The second language tag.</param>
/// <returns>true when the tags are equal ignoring case; otherwise, false.</returns>
public static bool AreEquivalent(string tag1, string tag2) =>
string.Equals(tag1, tag2, StringComparison.OrdinalIgnoreCase);

/// <summary>
/// Normalizes and compares two language tags for equivalence.
/// </summary>
/// <param name="tag1">The first language tag.</param>
/// <param name="tag2">The second language tag.</param>
/// <returns>true when both tags can be parsed and normalize to the same value; otherwise, false.</returns>
public static bool AreEquivalentNormalized(string tag1, string tag2)
{
LanguageTag? parsed1 = LanguageTag.Parse(tag1)?.Normalize();
Expand Down
Loading
Loading