Skip to content

Commit 295f0af

Browse files
maxkatz6Mpdreamz
andauthored
Allow EditorConfigFile to be used in-memory without access or use of 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]>
1 parent b3e00ba commit 295f0af

File tree

5 files changed

+96
-12
lines changed

5 files changed

+96
-12
lines changed

src/EditorConfig.Core/ConfigSection.cs

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public ConfigSection(string name, IEditorConfigFile origin, Dictionary<string, s
7070

7171
private static string FixGlob(string glob, string directory)
7272
{
73+
if (string.IsNullOrEmpty(directory)) return glob;
74+
7375
switch (glob.IndexOf('/'))
7476
{
7577
case -1: glob = "**/" + glob; break;

src/EditorConfig.Core/EditorConfigFile.cs

+31-7
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,49 @@ public class EditorConfigFile : IEditorConfigFile
6262
/// <inheritdoc cref="IEditorConfigFile.IsRoot"/>
6363
public bool IsRoot => _isRoot;
6464

65-
internal EditorConfigFile(string path, string cacheKey = null)
65+
private EditorConfigFile(
66+
string fileName,
67+
string directory,
68+
TextReader reader,
69+
string cacheKey = null)
6670
{
67-
Directory = Path.GetDirectoryName(path);
68-
FileName = Path.GetFileName(path);
71+
Directory = directory;
72+
FileName = fileName;
6973
CacheKey = cacheKey;
70-
Parse(path);
74+
ReadAndParse(reader);
7175

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

76-
private void Parse(string file)
80+
/// <summary> Parses EditorConfig file from the file path. </summary>
81+
/// <param name="path"> File path in a physical file system. </param>
82+
/// <returns> Parsed EditorConfig file. </returns>
83+
public static EditorConfigFile Parse(string path) => Parse(path, cacheKey: null);
84+
85+
/// <summary> Parses EditorConfig file from the text reader. </summary>
86+
/// <param name="reader"> Text reader. </param>
87+
/// <param name="directory"> EditorConfig base directory to match file sections to. Default is null. </param>
88+
/// <param name="fileName"> EditorConfig file name. Default is '.editorconfig'. </param>
89+
/// <returns> Parsed EditorConfig file. </returns>
90+
public static EditorConfigFile Parse(TextReader reader, string directory = null, string fileName = ".editorconfig") =>
91+
new(fileName, directory, reader);
92+
93+
internal static EditorConfigFile Parse(string path, string cacheKey)
7794
{
78-
var lines = File.ReadLines(file);
95+
using var file = File.OpenRead(path);
96+
using var reader = new StreamReader(file);
97+
return new EditorConfigFile(
98+
Path.GetFileName(path), Path.GetDirectoryName(path),
99+
reader, cacheKey);
100+
}
79101

102+
private void ReadAndParse(TextReader reader)
103+
{
80104
var activeDict = _globalDict;
81105
var sectionName = string.Empty;
82106
var reset = false;
83-
foreach (var line in lines)
107+
while (reader.ReadLine() is { } line)
84108
{
85109
if (string.IsNullOrWhiteSpace(line)) continue;
86110

src/EditorConfig.Core/EditorConfigFileCache.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ private static string GetFileHash(string filename)
2828
/// <returns></returns>
2929
public static EditorConfigFile GetOrCreate(string file)
3030
{
31-
if (!File.Exists(file)) return new EditorConfigFile(file);
31+
if (!File.Exists(file)) return EditorConfigFile.Parse(file);
3232

3333
var key = $"{file}_{GetFileHash(file)}";
34-
return FileCache.GetOrAdd(key, _ => new EditorConfigFile(file, key));
34+
return FileCache.GetOrAdd(key, _ => EditorConfigFile.Parse(file, key));
3535
}
3636
}

src/EditorConfig.Core/EditorConfigParser.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class EditorConfigParser
4141
/// <param name="configFileName">The name of the file(s) holding the editorconfiguration values</param>
4242
/// <param name="developmentVersion">Only used in testing, development to pass an older version to the parsing routine</param>
4343
public EditorConfigParser(string configFileName = ".editorconfig", Version developmentVersion = null)
44-
: this(f => new EditorConfigFile(f), configFileName, developmentVersion)
44+
: this(EditorConfigFile.Parse, configFileName, developmentVersion)
4545
{
4646

4747
}
@@ -91,13 +91,13 @@ public FileConfiguration Parse(string fileName, IEnumerable<EditorConfigFile> ed
9191
var sections =
9292
from configFile in editorConfigFiles
9393
from section in configFile.Sections
94-
where IsMatch(section.Glob, fullPath, configFile.Directory)
94+
where IsMatch(section.Glob, fullPath)
9595
select section;
9696

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

100-
private bool IsMatch(string glob, string fileName, string directory)
100+
private bool IsMatch(string glob, string fileName)
101101
{
102102
var matcher = GlobMatcher.Create(glob, _globOptions);
103103
var isMatch = matcher.IsMatch(fileName);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.IO;
2+
using EditorConfig.Core;
3+
using FluentAssertions;
4+
using Microsoft.VisualBasic;
5+
using NUnit.Framework;
6+
7+
namespace EditorConfig.Tests.InMemory
8+
{
9+
[TestFixture]
10+
public class CachingTests : EditorConfigTestBase
11+
{
12+
[Test]
13+
public void InMemoryConfigIsUsable()
14+
{
15+
var configContent = @"""
16+
root = true
17+
18+
[*.cs]
19+
end_of_line = lf
20+
""";
21+
var stringReader = new StringReader(configContent);
22+
var editorConfigFile = EditorConfigFile.Parse(stringReader);
23+
24+
var parser = new EditorConfigParser();
25+
var config = parser.Parse("myfile.cs", new[] { editorConfigFile });
26+
27+
config.EditorConfigFiles.Should().ContainSingle(f => f.IsRoot);
28+
config.EndOfLine.Should().Be(EndOfLine.LF);
29+
}
30+
31+
[Test]
32+
public void InMemoryConfigIsUsableWithVirtualPath()
33+
{
34+
var virtualDirectory = Path.Combine(Directory.GetDirectoryRoot("."), "VirtualPath");
35+
36+
var configContent = @"""
37+
root = true
38+
39+
[*.cs]
40+
end_of_line = lf
41+
""";
42+
var stringReader = new StringReader(configContent);
43+
var editorConfigFile = EditorConfigFile.Parse(stringReader, virtualDirectory);
44+
45+
var parser = new EditorConfigParser();
46+
47+
var file = Path.Combine(virtualDirectory, "myfile.cs");
48+
var config1 = parser.Parse(file, new[] { editorConfigFile });
49+
config1.EditorConfigFiles.Should().ContainSingle(f => f.IsRoot);
50+
config1.EndOfLine.Should().Be(EndOfLine.LF);
51+
52+
var directoryOutOfScope = Path.Combine(Directory.GetDirectoryRoot("."), "DifferentDirectory");
53+
var fileOutOfScope = Path.Combine(directoryOutOfScope, "myfile.cs");
54+
var config2 = parser.Parse(fileOutOfScope, new[] { editorConfigFile });
55+
config2.EditorConfigFiles.Should().BeEmpty();
56+
}
57+
}
58+
}

0 commit comments

Comments
 (0)