Skip to content

Commit

Permalink
Allow EditorConfigFile to be used in-memory without access or use of …
Browse files Browse the repository at this point in the history
…physical file system. (#25)

* Make it possible to create EditorConfigFile file from the memory + add tests

* Allow editor config without base directory

* fix test to work on non windows OS's

---------

Co-authored-by: Martijn Laarman <[email protected]>
  • Loading branch information
maxkatz6 and Mpdreamz authored Nov 24, 2023
1 parent b3e00ba commit 295f0af
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/EditorConfig.Core/ConfigSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public ConfigSection(string name, IEditorConfigFile origin, Dictionary<string, s

private static string FixGlob(string glob, string directory)
{
if (string.IsNullOrEmpty(directory)) return glob;

switch (glob.IndexOf('/'))
{
case -1: glob = "**/" + glob; break;
Expand Down
38 changes: 31 additions & 7 deletions src/EditorConfig.Core/EditorConfigFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,49 @@ public class EditorConfigFile : IEditorConfigFile
/// <inheritdoc cref="IEditorConfigFile.IsRoot"/>
public bool IsRoot => _isRoot;

internal EditorConfigFile(string path, string cacheKey = null)
private EditorConfigFile(
string fileName,
string directory,
TextReader reader,
string cacheKey = null)
{
Directory = Path.GetDirectoryName(path);
FileName = Path.GetFileName(path);
Directory = directory;
FileName = fileName;
CacheKey = cacheKey;
Parse(path);
ReadAndParse(reader);

if (_globalDict.TryGetValue("root", out var value))
bool.TryParse(value, out _isRoot);
}

private void Parse(string file)
/// <summary> Parses EditorConfig file from the file path. </summary>
/// <param name="path"> File path in a physical file system. </param>
/// <returns> Parsed EditorConfig file. </returns>
public static EditorConfigFile Parse(string path) => Parse(path, cacheKey: null);

/// <summary> Parses EditorConfig file from the text reader. </summary>
/// <param name="reader"> Text reader. </param>
/// <param name="directory"> EditorConfig base directory to match file sections to. Default is null. </param>
/// <param name="fileName"> EditorConfig file name. Default is '.editorconfig'. </param>
/// <returns> Parsed EditorConfig file. </returns>
public static EditorConfigFile Parse(TextReader reader, string directory = null, string fileName = ".editorconfig") =>
new(fileName, directory, reader);

internal static EditorConfigFile Parse(string path, string cacheKey)
{
var lines = File.ReadLines(file);
using var file = File.OpenRead(path);
using var reader = new StreamReader(file);
return new EditorConfigFile(
Path.GetFileName(path), Path.GetDirectoryName(path),
reader, cacheKey);
}

private void ReadAndParse(TextReader reader)
{
var activeDict = _globalDict;
var sectionName = string.Empty;
var reset = false;
foreach (var line in lines)
while (reader.ReadLine() is { } line)
{
if (string.IsNullOrWhiteSpace(line)) continue;

Expand Down
4 changes: 2 additions & 2 deletions src/EditorConfig.Core/EditorConfigFileCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ private static string GetFileHash(string filename)
/// <returns></returns>
public static EditorConfigFile GetOrCreate(string file)
{
if (!File.Exists(file)) return new EditorConfigFile(file);
if (!File.Exists(file)) return EditorConfigFile.Parse(file);

var key = $"{file}_{GetFileHash(file)}";
return FileCache.GetOrAdd(key, _ => new EditorConfigFile(file, key));
return FileCache.GetOrAdd(key, _ => EditorConfigFile.Parse(file, key));
}
}
6 changes: 3 additions & 3 deletions src/EditorConfig.Core/EditorConfigParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class EditorConfigParser
/// <param name="configFileName">The name of the file(s) holding the editorconfiguration values</param>
/// <param name="developmentVersion">Only used in testing, development to pass an older version to the parsing routine</param>
public EditorConfigParser(string configFileName = ".editorconfig", Version developmentVersion = null)
: this(f => new EditorConfigFile(f), configFileName, developmentVersion)
: this(EditorConfigFile.Parse, configFileName, developmentVersion)
{

}
Expand Down Expand Up @@ -91,13 +91,13 @@ public FileConfiguration Parse(string fileName, IEnumerable<EditorConfigFile> ed
var sections =
from configFile in editorConfigFiles
from section in configFile.Sections
where IsMatch(section.Glob, fullPath, configFile.Directory)
where IsMatch(section.Glob, fullPath)
select section;

return new FileConfiguration(ParseVersion, file, sections.ToList());
}

private bool IsMatch(string glob, string fileName, string directory)
private bool IsMatch(string glob, string fileName)
{
var matcher = GlobMatcher.Create(glob, _globOptions);
var isMatch = matcher.IsMatch(fileName);
Expand Down
58 changes: 58 additions & 0 deletions src/EditorConfig.Tests/InMemory/InMemoryConfigTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.IO;
using EditorConfig.Core;
using FluentAssertions;
using Microsoft.VisualBasic;
using NUnit.Framework;

namespace EditorConfig.Tests.InMemory
{
[TestFixture]
public class CachingTests : EditorConfigTestBase
{
[Test]
public void InMemoryConfigIsUsable()
{
var configContent = @"""
root = true
[*.cs]
end_of_line = lf
""";
var stringReader = new StringReader(configContent);
var editorConfigFile = EditorConfigFile.Parse(stringReader);

var parser = new EditorConfigParser();
var config = parser.Parse("myfile.cs", new[] { editorConfigFile });

config.EditorConfigFiles.Should().ContainSingle(f => f.IsRoot);
config.EndOfLine.Should().Be(EndOfLine.LF);
}

[Test]
public void InMemoryConfigIsUsableWithVirtualPath()
{
var virtualDirectory = Path.Combine(Directory.GetDirectoryRoot("."), "VirtualPath");

var configContent = @"""
root = true
[*.cs]
end_of_line = lf
""";
var stringReader = new StringReader(configContent);
var editorConfigFile = EditorConfigFile.Parse(stringReader, virtualDirectory);

var parser = new EditorConfigParser();

var file = Path.Combine(virtualDirectory, "myfile.cs");
var config1 = parser.Parse(file, new[] { editorConfigFile });
config1.EditorConfigFiles.Should().ContainSingle(f => f.IsRoot);
config1.EndOfLine.Should().Be(EndOfLine.LF);

var directoryOutOfScope = Path.Combine(Directory.GetDirectoryRoot("."), "DifferentDirectory");
var fileOutOfScope = Path.Combine(directoryOutOfScope, "myfile.cs");
var config2 = parser.Parse(fileOutOfScope, new[] { editorConfigFile });
config2.EditorConfigFiles.Should().BeEmpty();
}
}
}

0 comments on commit 295f0af

Please sign in to comment.