initial commit
This commit is contained in:
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Build results
|
||||||
|
[Bb]in/
|
||||||
|
.bin/
|
||||||
|
[Dd]ebug/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
[Oo]bj/
|
||||||
|
[Oo]ut/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
[Pp]ublish/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
.vshistory/
|
||||||
|
.idea/
|
||||||
|
.editorconfig
|
||||||
|
*.user
|
||||||
|
*.DotSettings
|
||||||
|
|
||||||
|
# temp files
|
||||||
|
.old*/
|
||||||
|
old/
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
16
CSharpScript.sln
Normal file
16
CSharpScript.sln
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpScript", "CSharpScript\CSharpScript.csproj", "{4A25F563-E6D4-4A4D-82B4-21A1E7FADC8A}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{4A25F563-E6D4-4A4D-82B4-21A1E7FADC8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4A25F563-E6D4-4A4D-82B4-21A1E7FADC8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4A25F563-E6D4-4A4D-82B4-21A1E7FADC8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4A25F563-E6D4-4A4D-82B4-21A1E7FADC8A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
23
CSharpScript/CSharpScript.csproj
Normal file
23
CSharpScript/CSharpScript.csproj
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" />
|
||||||
|
<PackageReference Include="OneOf" Version="3.0.271" />
|
||||||
|
<PackageReference Include="Tomlyn" Version="2.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="CSharpScript.toml.default">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
18
CSharpScript/CSharpScript.toml.default
Normal file
18
CSharpScript/CSharpScript.toml.default
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# where to search for .cs files
|
||||||
|
src_dir = "src"
|
||||||
|
|
||||||
|
# namespace and class with method Main
|
||||||
|
main_class = "Script.Program"
|
||||||
|
|
||||||
|
# where compiled files will be cached
|
||||||
|
cache_dir = "cache/CSharpScript"
|
||||||
|
|
||||||
|
# comppiled file name
|
||||||
|
assembly_file_name = "Program.dll"
|
||||||
|
|
||||||
|
# paths of dll files that are referenced in your source code
|
||||||
|
# Placeholders:
|
||||||
|
# %RUNTIME% - directory where System.* libraries are stored
|
||||||
|
references = [
|
||||||
|
"%RUNTIME%/netstandard.dll"
|
||||||
|
]
|
||||||
191
CSharpScript/Program.cs
Normal file
191
CSharpScript/Program.cs
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Tomlyn;
|
||||||
|
|
||||||
|
namespace CSharpScript;
|
||||||
|
|
||||||
|
public class Config
|
||||||
|
{
|
||||||
|
public string src_dir { get; set; }
|
||||||
|
public string main_class { get; set; }
|
||||||
|
public string cache_dir { get; set; } = "cache/CSharpScript";
|
||||||
|
public string assembly_file_name { get; set; }
|
||||||
|
|
||||||
|
public List<string> references { get; set; } = ["%RUNTIME%/netstandard.dll"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
private const string CONFIG_PATH = "CSharpScript.toml";
|
||||||
|
|
||||||
|
private static readonly TomlSerializerOptions TomlSerializerOptions = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
IndentSize = 4,
|
||||||
|
MaxDepth = 64,
|
||||||
|
DefaultIgnoreCondition = TomlIgnoreCondition.WhenWritingNull,
|
||||||
|
};
|
||||||
|
|
||||||
|
static string PathFixSeparators(string path)
|
||||||
|
{
|
||||||
|
char sep = Path.DirectorySeparatorChar;
|
||||||
|
char notSep = sep == '\\' ? '/' : '\\';
|
||||||
|
return path.Replace(notSep, sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
if (!File.Exists(CONFIG_PATH))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: Config '{CONFIG_PATH}' not found");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf = TomlSerializer.Deserialize<Config>(File.ReadAllText(CONFIG_PATH), TomlSerializerOptions)
|
||||||
|
?? throw new Exception("can't deserialize config " + CONFIG_PATH);
|
||||||
|
|
||||||
|
// find sources
|
||||||
|
var csFiles = Directory.GetFiles(PathFixSeparators(conf.src_dir), "*.cs").ToList();
|
||||||
|
Console.WriteLine("Source files:");
|
||||||
|
csFiles.ForEach(Console.WriteLine);
|
||||||
|
Console.WriteLine("");
|
||||||
|
|
||||||
|
// try load cached assembly
|
||||||
|
string cachedAssemblyPath = Path.GetFullPath(
|
||||||
|
Path.Combine(
|
||||||
|
PathFixSeparators(conf.cache_dir),
|
||||||
|
conf.assembly_file_name));
|
||||||
|
if (File.Exists(cachedAssemblyPath))
|
||||||
|
{
|
||||||
|
var sourceFileUpdateTime = csFiles.Select(File.GetLastWriteTime).Max();
|
||||||
|
var cachedAssemblyUpdateTime = File.GetLastWriteTime(cachedAssemblyPath);
|
||||||
|
Console.WriteLine($"Sources in '{conf.src_dir}' were updated at {sourceFileUpdateTime:yyyy.MM.dd-HH:mm:ss}");
|
||||||
|
Console.WriteLine($"Assembly '{cachedAssemblyPath}' was cached at {cachedAssemblyUpdateTime:yyyy.MM.dd-HH:mm:ss}");
|
||||||
|
// sources weren't changed
|
||||||
|
if (sourceFileUpdateTime <= cachedAssemblyUpdateTime)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Loading cached assembly '{cachedAssemblyPath}'");
|
||||||
|
var assembly = Assembly.LoadFile(cachedAssemblyPath);
|
||||||
|
return ExecAssemblyMain(assembly, conf, args);
|
||||||
|
}
|
||||||
|
Console.WriteLine("Cached assembly is obsolete");
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile sources
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
var compiler = InitCompiler(conf);
|
||||||
|
Console.WriteLine($"Initialized in {sw.ElapsedMilliseconds}ms");
|
||||||
|
sw.Restart();
|
||||||
|
Console.WriteLine("Compiling...");
|
||||||
|
var compResult = compiler.Compile(csFiles);
|
||||||
|
Console.WriteLine($"Compiled in {sw.ElapsedMilliseconds}s");
|
||||||
|
sw.Stop();
|
||||||
|
return compResult.Match(
|
||||||
|
noChanges => throw new NotImplementedException(noChanges.ToString()),
|
||||||
|
success =>
|
||||||
|
{
|
||||||
|
PrintDiagnostics(success.Diagnostics);
|
||||||
|
Console.WriteLine();
|
||||||
|
var assembly = SaveAndLoadCompiledAssembly(success, cachedAssemblyPath);
|
||||||
|
return ExecAssemblyMain(assembly, conf, args);
|
||||||
|
},
|
||||||
|
error =>
|
||||||
|
{
|
||||||
|
PrintDiagnostics(error.Diagnostics);
|
||||||
|
Console.WriteLine("Compilation failed.");
|
||||||
|
return 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static RoslynWrapper InitCompiler(Config conf)
|
||||||
|
{
|
||||||
|
Console.WriteLine("\nInitializing compiler...");
|
||||||
|
|
||||||
|
Console.WriteLine("Referenced assemblies:");
|
||||||
|
List<string> referenceAssemblyFilePaths = AppDomain.CurrentDomain
|
||||||
|
.GetAssemblies()
|
||||||
|
.Where(a => !a.IsDynamic && !string.IsNullOrEmpty(a.Location))
|
||||||
|
.Select(a => a.Location)
|
||||||
|
.ToList();
|
||||||
|
referenceAssemblyFilePaths.ForEach(Console.WriteLine);
|
||||||
|
|
||||||
|
var systemDllPath = referenceAssemblyFilePaths
|
||||||
|
.Find(p => Path.GetFileName(p).StartsWith("System.Private."))
|
||||||
|
?? throw new Exception("can't find location of System.dll in loaded assenmblies");
|
||||||
|
string runtimeAssembliesDir = Path.GetDirectoryName(systemDllPath) ?? ".";
|
||||||
|
foreach (var path_ in conf.references)
|
||||||
|
{
|
||||||
|
string path = Path.GetFullPath(
|
||||||
|
path_.Replace("%RUNTIME%", runtimeAssembliesDir));
|
||||||
|
referenceAssemblyFilePaths.Add(path);
|
||||||
|
Console.WriteLine(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
var referencedAssemblies = referenceAssemblyFilePaths
|
||||||
|
.Select(p => (MetadataReference)MetadataReference.CreateFromFile(p))
|
||||||
|
.ToList();
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
var compiler = new RoslynWrapper(conf.assembly_file_name, referencedAssemblies);
|
||||||
|
compiler.CompilationOptions = compiler.CompilationOptions
|
||||||
|
.WithOutputKind(OutputKind.ConsoleApplication)
|
||||||
|
.WithMainTypeName(conf.main_class);
|
||||||
|
return compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintDiagnostics(IEnumerable<Diagnostic> diagnostics)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Diagnostic messages:");
|
||||||
|
foreach (var d in diagnostics)
|
||||||
|
{
|
||||||
|
var span = d.Location.GetLineSpan();
|
||||||
|
var start = span.StartLinePosition;
|
||||||
|
string? locationString = null;
|
||||||
|
if (!string.IsNullOrEmpty(span.Path))
|
||||||
|
locationString = $"{span.Path}:{start.Line + 1}:{start.Character + 1} ";
|
||||||
|
Console.WriteLine($"{locationString}{d.Severity} {d.Id}: {d.GetMessage()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Assembly SaveAndLoadCompiledAssembly(CompilationSuccess success, string filePath)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Saving compiled assembly to '{filePath}'");
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath) ?? ".");
|
||||||
|
File.WriteAllBytes(filePath, success.AssemblyBytes);
|
||||||
|
Console.WriteLine("Loading compiled assembly from memory");
|
||||||
|
var assembly = Assembly.Load(success.AssemblyBytes);
|
||||||
|
return assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ExecAssemblyMain(Assembly assembly, Config conf, string[] args)
|
||||||
|
{
|
||||||
|
MethodInfo? entryPoint = assembly.EntryPoint;
|
||||||
|
if (entryPoint == null)
|
||||||
|
throw new Exception($"No entry point found in assembly {assembly} ");
|
||||||
|
|
||||||
|
// Main()
|
||||||
|
object?[]? parameters = null;
|
||||||
|
// Main(string[] args)
|
||||||
|
if (entryPoint.GetParameters().Length == 1)
|
||||||
|
{
|
||||||
|
parameters = [ args ];
|
||||||
|
}
|
||||||
|
|
||||||
|
string? argsStr = null;
|
||||||
|
if (args.Length != 0)
|
||||||
|
{
|
||||||
|
argsStr = "'" + string.Join("', '", args) + "'";
|
||||||
|
}
|
||||||
|
Console.WriteLine($"Executing {conf.assembly_file_name} {conf.main_class}.Main({argsStr})\n");
|
||||||
|
object? result = entryPoint.Invoke(null, parameters);
|
||||||
|
if (result is int retcode)
|
||||||
|
return retcode;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
173
CSharpScript/RoslynWrapper.cs
Normal file
173
CSharpScript/RoslynWrapper.cs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
using OneOf;
|
||||||
|
|
||||||
|
namespace CSharpScript;
|
||||||
|
|
||||||
|
// Based on https://github.com/tugberkugurlu/DotNetSamples/blob/master/csharp/RoslynCompileSample/RoslynCompileSample/Program.cs
|
||||||
|
|
||||||
|
public enum CompilationStatus
|
||||||
|
{
|
||||||
|
Compiled, NoChanges, Error
|
||||||
|
}
|
||||||
|
|
||||||
|
public record CompilationNoChanges;
|
||||||
|
public record CompilationSuccess(IList<Diagnostic> Diagnostics, byte[] AssemblyBytes);
|
||||||
|
public record CompilationError(IList<Diagnostic> Diagnostics);
|
||||||
|
|
||||||
|
|
||||||
|
public class RoslynWrapper
|
||||||
|
{
|
||||||
|
public readonly string AssemblyName;
|
||||||
|
|
||||||
|
public List<MetadataReference> References;
|
||||||
|
|
||||||
|
public CSharpParseOptions ParseOptions = new(
|
||||||
|
languageVersion: LanguageVersion.Latest);
|
||||||
|
|
||||||
|
public CSharpCompilationOptions CompilationOptions = new(
|
||||||
|
outputKind: OutputKind.DynamicallyLinkedLibrary);
|
||||||
|
|
||||||
|
|
||||||
|
private record CachedSourceFile(DateTime EditTime, CSharpSyntaxTree SyntaxTree);
|
||||||
|
|
||||||
|
private Dictionary<string, CachedSourceFile> _cachedSourceFiles = new();
|
||||||
|
|
||||||
|
private CSharpCompilation? _cachedCompilation;
|
||||||
|
|
||||||
|
|
||||||
|
public RoslynWrapper(string assemblyName, List<MetadataReference> references)
|
||||||
|
{
|
||||||
|
AssemblyName = assemblyName;
|
||||||
|
References = references;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public OneOf<CompilationNoChanges, CompilationSuccess, CompilationError> Compile(IEnumerable<string> filePaths)
|
||||||
|
{
|
||||||
|
if(_cachedCompilation is null)
|
||||||
|
CreateCompilation(filePaths);
|
||||||
|
else if (UpdateCompilation(filePaths) == false)
|
||||||
|
return new CompilationNoChanges();
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
var result = _cachedCompilation!.Emit(ms);
|
||||||
|
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
ms.Seek(0, SeekOrigin.Begin);
|
||||||
|
return new CompilationSuccess(result.Diagnostics, ms.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
var errors = result.Diagnostics
|
||||||
|
.Where(d =>
|
||||||
|
d.IsWarningAsError ||
|
||||||
|
d.Severity == DiagnosticSeverity.Error)
|
||||||
|
.ToList();
|
||||||
|
return new CompilationError(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static SourceText ReadSourceText(string filePath)
|
||||||
|
{
|
||||||
|
using var fileStream = File.OpenRead(filePath);
|
||||||
|
return SourceText.From(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateCompilation(IEnumerable<string> filePaths)
|
||||||
|
{
|
||||||
|
Dictionary<string, CachedSourceFile> sourceFiles = new();
|
||||||
|
List<SyntaxTree> syntaxTrees = new();
|
||||||
|
foreach (var filePath in filePaths)
|
||||||
|
{
|
||||||
|
var editTime = File.GetLastWriteTime(filePath);
|
||||||
|
var syntaxTree = CSharpSyntaxTree.ParseText(ReadSourceText(filePath), ParseOptions, filePath);
|
||||||
|
syntaxTrees.Add(syntaxTree);
|
||||||
|
sourceFiles.Add(filePath, new CachedSourceFile(editTime, (CSharpSyntaxTree)syntaxTree));
|
||||||
|
}
|
||||||
|
|
||||||
|
var compilation = CSharpCompilation.Create(
|
||||||
|
AssemblyName,
|
||||||
|
syntaxTrees: syntaxTrees,
|
||||||
|
references: References,
|
||||||
|
options: CompilationOptions);
|
||||||
|
|
||||||
|
// assign fields only when compilation is initialized successfully
|
||||||
|
_cachedCompilation = compilation;
|
||||||
|
_cachedSourceFiles = sourceFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>false if source code wasn't changed since last cached compilation</returns>
|
||||||
|
private bool UpdateCompilation(IEnumerable<string> filePaths)
|
||||||
|
{
|
||||||
|
if (_cachedCompilation is null)
|
||||||
|
throw new Exception("can't update compilation because it was not cached");
|
||||||
|
|
||||||
|
Dictionary<string, CachedSourceFile> cachedSourceFilesTemp = new(_cachedSourceFiles);
|
||||||
|
List<(CachedSourceFile old, CachedSourceFile upd)> sourceFilesChanged = new();
|
||||||
|
List<CachedSourceFile> sourceFilesNotChanged = new();
|
||||||
|
List<CachedSourceFile> sourceFilesAdded = new();
|
||||||
|
List<CachedSourceFile> sourceFilesRemoved = new();
|
||||||
|
|
||||||
|
// populate sourceFilesChanged, sourceFilesNotChanged, sourceFilesAdded
|
||||||
|
foreach (var filePath in filePaths)
|
||||||
|
{
|
||||||
|
var editTime = File.GetLastWriteTime(filePath);
|
||||||
|
// source file is cached
|
||||||
|
if(_cachedSourceFiles.TryGetValue(filePath, out var cachedFile))
|
||||||
|
{
|
||||||
|
cachedSourceFilesTemp.Remove(filePath);
|
||||||
|
|
||||||
|
// file wasn't edited
|
||||||
|
if (editTime == cachedFile.EditTime)
|
||||||
|
{
|
||||||
|
sourceFilesNotChanged.Add(cachedFile);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// file was edited
|
||||||
|
var updatedSyntaxTree = cachedFile.SyntaxTree.WithChangedText(ReadSourceText(filePath));
|
||||||
|
var updatedFile = new CachedSourceFile(editTime, (CSharpSyntaxTree)updatedSyntaxTree);
|
||||||
|
sourceFilesChanged.Add(new(cachedFile, updatedFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
// source file is new
|
||||||
|
var syntaxTree = CSharpSyntaxTree.ParseText(ReadSourceText(filePath), ParseOptions, filePath);
|
||||||
|
sourceFilesAdded.Add(new CachedSourceFile(editTime, (CSharpSyntaxTree)syntaxTree));
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point all wiles that are left in cachedSourceFilesTemp are not present in filePaths
|
||||||
|
sourceFilesRemoved.AddRange(cachedSourceFilesTemp.Values);
|
||||||
|
|
||||||
|
// source files are the same as in cached compilation, nothing to update
|
||||||
|
int changedFilesCount = sourceFilesChanged.Count + sourceFilesAdded.Count + sourceFilesRemoved.Count;
|
||||||
|
if (changedFilesCount == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Dictionary<string, CachedSourceFile> updatedCachedSourceFiles = new();
|
||||||
|
foreach (var csf in sourceFilesNotChanged)
|
||||||
|
updatedCachedSourceFiles.Add(csf.SyntaxTree.FilePath, csf);
|
||||||
|
foreach (var csf in sourceFilesAdded)
|
||||||
|
updatedCachedSourceFiles.Add(csf.SyntaxTree.FilePath, csf);
|
||||||
|
foreach (var tupl in sourceFilesChanged)
|
||||||
|
updatedCachedSourceFiles.Add(tupl.upd.SyntaxTree.FilePath, tupl.upd);
|
||||||
|
|
||||||
|
var updatedCompilation = _cachedCompilation
|
||||||
|
.RemoveSyntaxTrees(sourceFilesRemoved.Select(csf => csf.SyntaxTree))
|
||||||
|
.AddSyntaxTrees(sourceFilesAdded.Select(csf => csf.SyntaxTree));
|
||||||
|
foreach (var (old, upd) in sourceFilesChanged)
|
||||||
|
{
|
||||||
|
updatedCompilation = updatedCompilation.ReplaceSyntaxTree(old.SyntaxTree, upd.SyntaxTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign fields only when compilation is modified successfully
|
||||||
|
_cachedCompilation = updatedCompilation;
|
||||||
|
_cachedSourceFiles = updatedCachedSourceFiles;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user