Ejemplo n.º 1
0
        /// <summary>
        /// Parses an editor config file text located at the given path. No parsing
        /// errors are reported. If any line contains a parse error, it is dropped.
        /// </summary>
        public static AnalyzerConfig Parse(SourceText text, string?pathToFile)
        {
            if (pathToFile is null || !Path.IsPathRooted(pathToFile) || string.IsNullOrEmpty(Path.GetFileName(pathToFile)))
            {
                throw new ArgumentException("Must be an absolute path to an editorconfig file", nameof(pathToFile));
            }

            Section?globalSection       = null;
            var     namedSectionBuilder = ImmutableArray.CreateBuilder <Section>();

            // N.B. The editorconfig documentation is quite loose on property interpretation.
            // Specifically, it says:
            //      Currently all properties and values are case-insensitive.
            //      They are lowercased when parsed.
            // To accommodate this, we use a lower case Unicode mapping when adding to the
            // dictionary, but we also use a case-insensitive key comparer when doing lookups
            var activeSectionProperties = ImmutableDictionary.CreateBuilder <string, string>(
                Section.PropertiesKeyComparer);
            string activeSectionName = "";

            foreach (var textLine in text.Lines)
            {
                string line = textLine.ToString();

                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }

                if (IsComment(line))
                {
                    continue;
                }

                var sectionMatches = s_sectionMatcher.Matches(line);
                if (sectionMatches.Count > 0 && sectionMatches[0].Groups.Count > 0)
                {
                    addNewSection();

                    var sectionName = sectionMatches[0].Groups[1].Value;
                    Debug.Assert(!string.IsNullOrEmpty(sectionName));

                    activeSectionName       = sectionName;
                    activeSectionProperties = ImmutableDictionary.CreateBuilder <string, string>(
                        Section.PropertiesKeyComparer);
                    continue;
                }

                var propMatches = s_propertyMatcher.Matches(line);
                if (propMatches.Count > 0 && propMatches[0].Groups.Count > 1)
                {
                    var key   = propMatches[0].Groups[1].Value;
                    var value = propMatches[0].Groups[2].Value;

                    Debug.Assert(!string.IsNullOrEmpty(key));
                    Debug.Assert(key == key.Trim());
                    Debug.Assert(value == value?.Trim());

                    key = CaseInsensitiveComparison.ToLower(key);
                    if (ReservedKeys.Contains(key) || ReservedValues.Contains(value))
                    {
                        value = CaseInsensitiveComparison.ToLower(value);
                    }

                    activeSectionProperties[key] = value ?? "";
                    continue;
                }
            }

            // Add the last section
            addNewSection();

            return(new AnalyzerConfig(globalSection !, namedSectionBuilder.ToImmutable(), pathToFile));

            void addNewSection()
            {
                // Close out the previous section
                var previousSection = new Section(activeSectionName, activeSectionProperties.ToImmutable());

                if (activeSectionName == "")
                {
                    // This is the global section
                    globalSection = previousSection;
                }
                else
                {
                    namedSectionBuilder.Add(previousSection);
                }
            }
        }
Ejemplo n.º 2
0
 internal static bool Equals(string lhs, string rhs)
 {
     return(CaseInsensitiveComparison.Equals(lhs, rhs));
 }