diff --git a/cs/Markdown/Markdown.csproj b/cs/Markdown/Markdown.csproj new file mode 100644 index 000000000..2f4fc7765 --- /dev/null +++ b/cs/Markdown/Markdown.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/cs/Markdown/Md.cs b/cs/Markdown/Md.cs new file mode 100644 index 000000000..d58cf0ad4 --- /dev/null +++ b/cs/Markdown/Md.cs @@ -0,0 +1,53 @@ +using Markdown.Tags; + +namespace Markdown; + +public static class Md +{ + private static IEnumerable Tags + { + get + { + yield return new EscapeMdTagKind(); + yield return new SingleMdTagKind("#", "

", "

"); + yield return new PairMdTagKind("_", "", ""); + yield return new PairMdTagKind("__", "", ""); + } + } + + private static IEnumerable, bool>> TagRules + { + get + { + yield return IgnoreIntersectionBetweenPairTagsRule; + yield return IgnorePairTagWhenParentPairTagHasGreaterLengthRule; + } + } + + public static string Render(string markdownText) + { + var root = new MdTokenizer(Tags.ToList(), TagRules).Tokenize(markdownText); + return root.ConvertToHtml(); + } + + private static bool IgnorePairTagWhenParentPairTagHasGreaterLengthRule(Token tokenToCheck, + IEnumerable tokens) => + tokenToCheck.Tag is PairMdTagKind + && tokens + .Where(t => t != tokenToCheck && t.Tag is PairMdTagKind) + .Any(parent => parent.IsChild(tokenToCheck) + && !(parent.Tag.MdTag.Length > tokenToCheck.Tag.MdTag.Length)); + + + private static bool IgnoreIntersectionBetweenPairTagsRule(Token tokenToCheck, IEnumerable tokens) => + tokenToCheck.Tag is PairMdTagKind + && tokens + .Where(t => t != tokenToCheck && t.Tag is PairMdTagKind) + .Any(t => IsIntersectionBetween(tokenToCheck, t) + || IsIntersectionBetween(t, tokenToCheck)); + + private static bool IsIntersectionBetween(Token token, Token otherToken) => + token.Position > otherToken.Position + && token.Position < otherToken.Position + otherToken.Value.Length + && token.Position + token.Value.Length > otherToken.Position + otherToken.Value.Length; +} \ No newline at end of file diff --git a/cs/Markdown/MdTokenizer.cs b/cs/Markdown/MdTokenizer.cs new file mode 100644 index 000000000..bdbcc7c16 --- /dev/null +++ b/cs/Markdown/MdTokenizer.cs @@ -0,0 +1,111 @@ +using Markdown.Models; +using Markdown.Tags; + +namespace Markdown; + +public class MdTokenizer(List tags, IEnumerable, bool>> tagRules) +{ + private readonly Dictionary availableTags = tags.ToDictionary(tag => tag.MdTag, tag => tag); + private readonly List, bool>> tagRules = tagRules.ToList(); + private readonly List mdLenOfTagSignatures = tags + .Select(tag => tag.MdTag.Length) + .Distinct() + .OrderDescending() + .ToList(); + + public Token Tokenize(string text) + { + var root = new Token(text); + + foreach (var line in GetLines(text)) + { + var tokens = GetTokens(line.Value).OrderBy(t => t.Position).ToList(); + foreach (var token in tokens + .Where(t => tagRules.Select(rule => rule(t, tokens)) + .All(result => !result))) line.AddToken(token); + root.AddToken(line); + } + + return root; + } + + private static IEnumerable GetLines(string text) + { + var position = 0; + foreach (var line in text.Split(Environment.NewLine)) + { + yield return new Token(line, position, new SingleMdTagKind()); + position += line.Length + Environment.NewLine.Length; + } + } + + private IEnumerable GetTokens(string text) + { + var tags = GetTags(text).ToList(); + var escapeTokens = ParseEscapedTokens(text, tags).ToList(); + + return ParseTokens(text, tags).Concat(escapeTokens); + } + + private IEnumerable GetTags(string text) + { + for (var position = 0; position < text.Length; position += 1) + { + if (!TryGetTag(text, position, out var tag)) continue; + + yield return new Tag(position, tag); + + position += tag.Length - 1; + } + } + + private bool TryGetTag(string text, int position, out IMdTagKind mdTag) + { + foreach (var mdLenOfTagSignature in mdLenOfTagSignatures) + { + if (position + mdLenOfTagSignature > text.Length || !availableTags + .TryGetValue(text.Substring(position, mdLenOfTagSignature), out var tag)) continue; + + mdTag = tag; + return true; + } + + mdTag = null!; + return false; + } + + private static IEnumerable ParseEscapedTokens(string text, List tags) + { + for (var idx = 0; idx < tags.Count - 1; idx += 1) + { + if (tags[idx].TagKind is not EscapeMdTagKind) continue; + + var position = tags[idx].Position; + tags.Remove(tags[idx]); + + if (tags[idx].Position - position == 1) + { + yield return text.CreateEscapeToken(tags[idx]); + tags.Remove(tags[idx]); + } + + idx -= 1; + } + } + + private static IEnumerable ParseTokens(string text, List tags) + { + for (var idx = 0; idx < tags.Count; idx += 1) + { + if (!tags[idx].TagKind.TryGetToken(text, tags[idx], tags, out var token, + out var closeToken)) continue; + + if (closeToken != null) tags.Remove(closeToken); + + yield return token; + tags.RemoveAt(idx); + + idx -= 1; + } + } +} \ No newline at end of file diff --git a/cs/Markdown/Models/Tag.cs b/cs/Markdown/Models/Tag.cs new file mode 100644 index 000000000..1763ff138 --- /dev/null +++ b/cs/Markdown/Models/Tag.cs @@ -0,0 +1,5 @@ +using Markdown.Tags; + +namespace Markdown.Models; + +public record Tag(int Position, IMdTagKind TagKind); \ No newline at end of file diff --git a/cs/Markdown/Program.cs b/cs/Markdown/Program.cs new file mode 100644 index 000000000..08a2138cf --- /dev/null +++ b/cs/Markdown/Program.cs @@ -0,0 +1,9 @@ +namespace Markdown; + +class Program +{ + public static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + } +} \ No newline at end of file diff --git a/cs/Markdown/StringExtension.cs b/cs/Markdown/StringExtension.cs new file mode 100644 index 000000000..6ad4f2a6b --- /dev/null +++ b/cs/Markdown/StringExtension.cs @@ -0,0 +1,44 @@ +using Markdown.Models; +using Markdown.Tags; + +namespace Markdown; + +public static class StringExtension +{ + public static bool IsSubstring(this string text, int position, string value, bool isForward = true) + { + if (isForward ? position + value.Length > text.Length : position - value.Length < 0) return false; + + var substring = isForward + ? text.Substring(position, value.Length) + : text.Substring(position - value.Length, value.Length); + + return substring == value; + } + + public static bool? IsSubstring(this string text, int position, Predicate predicate, bool isForward = true) + { + if (isForward ? position + 1 > text.Length : position - 1 < 0) return null; + + position = isForward ? position : position - 1; + return predicate(text[position]); + } + + public static Token CreateToken(this string text, int startIndex, int stopIndex, IMdTagKind tag) + { + var value = text.Substring(startIndex, stopIndex - startIndex); + return new Token(value, startIndex, tag); + } + + public static int GetEndOfLinePosition(this string text, int startIndex = 0) + { + var newLinePosition = text.IndexOf(Environment.NewLine, startIndex, StringComparison.Ordinal); + return newLinePosition != -1 ? newLinePosition + Environment.NewLine.Length : text.Length; + } + + public static Token CreateEscapeToken(this string text, Tag escapeTag) + { + var value = text.Substring(escapeTag.Position - 1, escapeTag.TagKind.Length); + return new Token(value, escapeTag.Position - 1, new EscapeMdTagKind()); + } +} \ No newline at end of file diff --git a/cs/Markdown/Tags/EscapeMdTagKind.cs b/cs/Markdown/Tags/EscapeMdTagKind.cs new file mode 100644 index 000000000..9631c7b09 --- /dev/null +++ b/cs/Markdown/Tags/EscapeMdTagKind.cs @@ -0,0 +1,38 @@ +using Markdown.Models; + +namespace Markdown.Tags; + +public class EscapeMdTagKind : IMdTagKind +{ + public string MdTag => "\\"; + public string HtmlOpenTag => string.Empty; + public string HtmlCloseTag => string.Empty; + + public bool TokenCanBeCreated(string text, int startIndex, int stopIndex) => + text.IsSubstring(startIndex, MdTag); + + public bool TryGetToken(string text, Tag openTag, List closeTags, out Token token, + out Tag closeTag) + { + var openTagIndex = closeTags.IndexOf(openTag); + var escapedTag = openTagIndex + 1 > closeTags.Count + ? null + : closeTags[openTagIndex + 1]; + + if (escapedTag != null) + { + closeTag = escapedTag; + token = text.CreateToken(openTag.Position, escapedTag.Position + + escapedTag.TagKind.Length, this); + return true; + } + + closeTag = null!; + token = null!; + return false; + } + + public string RemoveMdTags(string text) => text.Remove(0, MdTag.Length); + + public string InsertHtmlTags(string text) => text; +} \ No newline at end of file diff --git a/cs/Markdown/Tags/IMdTagKind.cs b/cs/Markdown/Tags/IMdTagKind.cs new file mode 100644 index 000000000..03c872800 --- /dev/null +++ b/cs/Markdown/Tags/IMdTagKind.cs @@ -0,0 +1,16 @@ +using Markdown.Models; + +namespace Markdown.Tags; + +public interface IMdTagKind +{ + public string MdTag { get; } + public string HtmlOpenTag { get; } + public string HtmlCloseTag { get; } + public int Length => MdTag.Length; + + public bool TokenCanBeCreated(string text, int startIndex, int stopIndex); + public bool TryGetToken(string text, Tag openTag, List closeTags, out Token token, out Tag? closeTag); + public string RemoveMdTags(string text); + public string InsertHtmlTags(string text); +} \ No newline at end of file diff --git a/cs/Markdown/Tags/PairMdTagKind.cs b/cs/Markdown/Tags/PairMdTagKind.cs new file mode 100644 index 000000000..581597ffa --- /dev/null +++ b/cs/Markdown/Tags/PairMdTagKind.cs @@ -0,0 +1,56 @@ +using Markdown.Models; + +namespace Markdown.Tags; + +public class PairMdTagKind(string mdTag, string htmlOpenTag, string htmlCloseTag) : IMdTagKind +{ + public string MdTag => mdTag; + public string HtmlOpenTag => htmlOpenTag; + public string HtmlCloseTag => htmlCloseTag; + + private bool IsValidTag(string text, int position) => + text.IsSubstring(position, MdTag) + && text.IsSubstring(position, char.IsDigit, false) != true + && text.IsSubstring(position + MdTag.Length, char.IsDigit) != true; + + public bool TokenCanBeCreated(string text, int startIndex, int stopIndex) + { + if (!IsValidTag(text, startIndex) || !IsValidTag(text, stopIndex - MdTag.Length)) return false; + + var value = text.Substring(startIndex, stopIndex - startIndex); + if (value.Split(' ').Length == 1) return value.Length > MdTag.Length * 2; + + return value.Split(Environment.NewLine).Length == 1 + && text.IsSubstring(startIndex, char.IsWhiteSpace, false) != false + && text.IsSubstring(stopIndex, char.IsWhiteSpace) != false; + } + + public bool TryGetToken(string text, Tag openTag, List closeTags, out Token token, out Tag closeTag) + { + foreach (var tag in closeTags.Where(t => openTag != t + && openTag.TagKind == t.TagKind + && openTag.Position <= t.Position + && openTag.TagKind.TokenCanBeCreated(text, openTag.Position, + t.Position + t.TagKind.Length))) + { + closeTag = tag; + token = text.CreateToken(openTag.Position, + tag.Position + tag.TagKind.Length, openTag.TagKind); + return true; + } + + closeTag = null!; + token = null!; + return false; + } + + public string RemoveMdTags(string text) => + text + .Remove(text.Length - MdTag.Length) + .Remove(0, MdTag.Length); + + public string InsertHtmlTags(string text) => + text + .Insert(text.Length, HtmlCloseTag) + .Insert(0, HtmlOpenTag); +} diff --git a/cs/Markdown/Tags/SingleMdTagKind.cs b/cs/Markdown/Tags/SingleMdTagKind.cs new file mode 100644 index 000000000..3c5e5b8f9 --- /dev/null +++ b/cs/Markdown/Tags/SingleMdTagKind.cs @@ -0,0 +1,47 @@ +using Markdown.Models; + +namespace Markdown.Tags; + +public class SingleMdTagKind(string mdTagKind, string htmlOpenTag, string htmlCloseTag) : IMdTagKind +{ + public string MdTag => mdTagKind; + public string HtmlOpenTag => htmlOpenTag; + public string HtmlCloseTag => htmlCloseTag; + + public SingleMdTagKind() : this(string.Empty, string.Empty, string.Empty) + { + } + + private bool IsValidTag(string text, int position) => + text.IsSubstring(position, MdTag) + && (text.IsSubstring(position, Environment.NewLine, false) || position == 0); + + public bool TokenCanBeCreated(string text, int startIndex, int stopIndex) => + IsValidTag(text, startIndex) + && (text.IsSubstring(stopIndex, Environment.NewLine, false) || text.Length == stopIndex); + + public bool TryGetToken(string text, Tag openTag, List closeTags, out Token token, + out Tag? closeTag) + { + var closeTagIndex = text.GetEndOfLinePosition(openTag.Position); + if (TokenCanBeCreated(text, openTag.Position, closeTagIndex)) + { + closeTag = null!; + token = text.CreateToken(openTag.Position, closeTagIndex, this); + return true; + } + + closeTag = null; + token = null!; + return false; + } + + public string RemoveMdTags(string text) => text.Remove(0, MdTag.Length); + + public string InsertHtmlTags(string text) => + text + .Insert(text.EndsWith(Environment.NewLine) + ? text.Length - Environment.NewLine.Length + : text.Length, HtmlCloseTag) + .Insert(0, HtmlOpenTag); +} \ No newline at end of file diff --git a/cs/Markdown/Token.cs b/cs/Markdown/Token.cs new file mode 100644 index 000000000..efc4a0b3b --- /dev/null +++ b/cs/Markdown/Token.cs @@ -0,0 +1,46 @@ +using System.Text; +using Markdown.Tags; + +namespace Markdown; + +public class Token(string value, int position, IMdTagKind mdTagKind) +{ + private readonly List children = []; + + public string Value => value; + public IMdTagKind Tag => mdTagKind; + public int Position { get; private set; } = position; + + public Token(string value) : this(value, value.Length, new SingleMdTagKind()) + { + } + + public void AddToken(Token child) + { + var parent = children.FirstOrDefault(token => token.IsChild(child)); + + if (parent != null) + { + parent.AddToken(child); + child.Position -= parent.Position + parent.Tag.MdTag.Length; + } + else children.Add(child); + } + + public string ConvertToHtml() + { + var sb = new StringBuilder(Tag.RemoveMdTags(Value)); + foreach (var child in children.OrderByDescending(token => token.Position)) + { + sb.Remove(child.Position, child.Value.Length); + sb.Insert(child.Position, child.ConvertToHtml()); + } + + return Tag.InsertHtmlTags(sb.ToString()); + } + + public bool IsChild(Token child) => + child.Position >= Position + && child.Position < Position + Value.Length + && child.Position + child.Value.Length <= Position + Value.Length; +} \ No newline at end of file diff --git a/cs/MarkdownTests/MarkdownTests.csproj b/cs/MarkdownTests/MarkdownTests.csproj new file mode 100644 index 000000000..4f4a35e3f --- /dev/null +++ b/cs/MarkdownTests/MarkdownTests.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + diff --git a/cs/MarkdownTests/MdTests.cs b/cs/MarkdownTests/MdTests.cs new file mode 100644 index 000000000..34bfa22d8 --- /dev/null +++ b/cs/MarkdownTests/MdTests.cs @@ -0,0 +1,126 @@ +using System.Diagnostics; +using Markdown; +using FluentAssertions; +namespace MarkdownTests; + +[TestFixture] +[TestOf(typeof(Md))] +public class MdTests +{ + [TestCaseSource(nameof(ConvertTagsTests))] + [TestCaseSource(nameof(MdSpecTests))] + public void Render_ShouldWorkCorrectly(string input, string expected) => Md.Render(input).Should().Be(expected); + + [TestCase(100, 10)] + [TestCase(100, 100)] + [TestCase(100, 1000)] + public void Render_ShouldWorkLinearly(int times, int inputScale) + { + const string input = "# Заголовок c _курсивным текстом_ и __полужирным текстом__"; + var scaledInput = string.Join(Environment.NewLine, Enumerable.Repeat(input, inputScale)); + var timeWithDefaultInput = MeasureRenderTime(input, times); + var timeWithScaledInput = MeasureRenderTime(scaledInput, times); + var avgWithDefaultInput = timeWithDefaultInput / times; + var avgWithScaledInput = timeWithScaledInput / (inputScale * times); + + avgWithDefaultInput.Should().BeCloseTo(avgWithScaledInput, TimeSpan.FromTicks(3300)); + } + + + private static TimeSpan MeasureRenderTime(string input, int times = 1) + { + var timer = new Stopwatch(); + + for (var i = 0; i < times; i++) + { + timer.Start(); + Md.Render(input); + timer.Stop(); + } + + return timer.Elapsed; + } + public static IEnumerable ConvertTagsTests + { + get + { + yield return new TestCaseData( + $"# Заголовок{Environment.NewLine}#Заголовок", $"

Заголовок

{Environment.NewLine}

Заголовок

") + .SetName("Render_ShouldConvertHeaderTag") + .SetCategory(nameof(ConvertTagsTests)); + yield return new TestCaseData( + $"_курсивный текст_{Environment.NewLine}_курсивный текст_", + $"курсивный текст{Environment.NewLine}курсивный текст") + .SetName("Render_ShouldConvertItalicTag") + .SetCategory(nameof(ConvertTagsTests)); + yield return new TestCaseData( + $"__полужирный текст__{Environment.NewLine}__полужирный текст__", + $"полужирный текст{Environment.NewLine}полужирный текст") + .SetName("Render_ShouldConvertBoldTag") + .SetCategory(nameof(ConvertTagsTests)); + yield return new TestCaseData( + "_чем\\_ 100_ __раз_ услышать.__", + "_чем_ 100_ __раз_ услышать.__") + .SetName("Render_ShouldConvertEscapeTag") + .SetCategory(nameof(ConvertTagsTests)); + yield return new TestCaseData( + "# Заголовок c _курсивным текстом_ и __полужирным текстом__", + "

Заголовок c курсивным текстом и полужирным текстом

") + .SetName("Render_ShouldConvertAllTagsInHeader") + .SetCategory(nameof(ConvertTagsTests)); + } + } + + public static IEnumerable MdSpecTests + { + get + { + yield return new TestCaseData( + $"#Это заголовок, а это (#) - нет.{Environment.NewLine}И это #тоже# не ##заголовок##", + $"

Это заголовок, а это (#) - нет.

{Environment.NewLine}И это #тоже# не ##заголовок##") + .SetName("Render_ShouldIgnoreSingleTags_WhenNotStartsWithNewLine") + .SetCategory("BasicSpec"); + yield return new TestCaseData( + $"Это _заголовок1_ ,а не заголовок __1 уровня__{Environment.NewLine}_4 Life CJ, _Grove __123__ Street_ 4 Life_", + $"Это _заголовок1_ ,а не заголовок __1 уровня__{Environment.NewLine}_4 Life CJ, Grove __123__ Street 4 Life_") + .SetName("Render_ShouldIgnorePairTags_WhenPlacedWithNumbers") + .SetCategory("MdSpec"); + yield return new TestCaseData( + $"Подчерки _мо_гут вы__де__лять ча_сть_ слова{Environment.NewLine}Но в разных словах не могут", + $"Подчерки могут выделять часть слова{Environment.NewLine}Но в разных словах не могут") + .SetName("Render_ShouldConvertPairTags_WhenPartOfWordMarked") + .SetCategory("MdSpec"); + yield return new TestCaseData( + $"Подчерки могут выделять часть слова{Environment.NewLine}Но в раз_ных сло_вах н__е мог__ут", + $"Подчерки могут выделять часть слова{Environment.NewLine}Но в раз_ных сло_вах н__е мог__ут") + .SetName("Render_ShouldIgnorePairTags_WhenPartsOfDifferentWordsMarked") + .SetCategory("MdSpec"); + yield return new TestCaseData( + $"За подчерками, _начинающими выделение,_ должен следовать __непробельный символ__{Environment.NewLine}Иначе_ ничего_ не__ получится!__", + $"За подчерками, начинающими выделение, должен следовать непробельный символ{Environment.NewLine}Иначе_ ничего_ не__ получится!__") + .SetName("Render_ShouldIgnorePairTags_WhenMarkedWordsStartsWithNonWhitespace") + .SetCategory("MdSpec"); + yield return new TestCaseData( + $"Подчерки, _заканчивающие выделение,_ должны следовать за __непробельным символом__{Environment.NewLine}_Иначе _ничего __не получится __!", + $"Подчерки, заканчивающие выделение, должны следовать за непробельным символом{Environment.NewLine}_Иначе _ничего __не получится __!") + .SetName("Render_ShouldIgnorePairTags_WhenMarkedWordsEndsWithNonWhitespace") + .SetCategory("MdSpec"); + yield return new TestCaseData( + "В случае __пересечения _двойных__ и одинарных_ подчерков ни _один из __них не_ считается__ выделением", + "В случае __пересечения _двойных__ и одинарных_ подчерков ни _один из __них не_ считается__ выделением") + .SetName("Render_ShouldIgnorePairTags_WhenIntersectionBetweenDifferentKindsOfPairTags") + .SetCategory("MdSpec"); + yield return new TestCaseData( + $"__Непарные _символы в рамках{Environment.NewLine}одного_ абзаца не считаются__ выделением", + $"__Непарные _символы в рамках{Environment.NewLine}одного_ абзаца не считаются__ выделением") + .SetName("Render_ShouldIgnorePairTags_WhenPlacedInMultiLine") + .SetCategory("MdSpec"); + yield return new TestCaseData( + "Если внутри подчерков пустая строка ____, то они остаются символами подчерка", + "Если внутри подчерков пустая строка ____, то они остаются символами подчерка") + .SetName("Render_ShouldIgnorePairTags_WhenTextIsEmpty") + .SetCategory("MdSpec"); + } + } +} + diff --git a/cs/clean-code.sln b/cs/clean-code.sln index 2206d54db..8b231a313 100644 --- a/cs/clean-code.sln +++ b/cs/clean-code.sln @@ -9,6 +9,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlDigit", "ControlDigi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdown", "Markdown\Markdown.csproj", "{71910566-C5DE-4C56-93E4-16976D5D4FC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownTests", "MarkdownTests\MarkdownTests.csproj", "{742ED593-BF23-4F69-BD7C-255777424108}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,5 +31,13 @@ Global {C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Release|Any CPU.Build.0 = Release|Any CPU + {71910566-C5DE-4C56-93E4-16976D5D4FC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71910566-C5DE-4C56-93E4-16976D5D4FC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71910566-C5DE-4C56-93E4-16976D5D4FC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71910566-C5DE-4C56-93E4-16976D5D4FC6}.Release|Any CPU.Build.0 = Release|Any CPU + {742ED593-BF23-4F69-BD7C-255777424108}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {742ED593-BF23-4F69-BD7C-255777424108}.Debug|Any CPU.Build.0 = Debug|Any CPU + {742ED593-BF23-4F69-BD7C-255777424108}.Release|Any CPU.ActiveCfg = Release|Any CPU + {742ED593-BF23-4F69-BD7C-255777424108}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/cs/clean-code.sln.DotSettings b/cs/clean-code.sln.DotSettings index 135b83ecb..229f449d2 100644 --- a/cs/clean-code.sln.DotSettings +++ b/cs/clean-code.sln.DotSettings @@ -1,6 +1,9 @@  <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + True True True Imported 10.10.2016