Skip to content

Commit

Permalink
better file parsing and overall logic
Browse files Browse the repository at this point in the history
  • Loading branch information
filipw committed Nov 23, 2016
1 parent 8f1cd8a commit 696a902
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 67 deletions.
70 changes: 21 additions & 49 deletions src/OmniSharp.Script/FileParser.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace OmniSharp.Script
{
// adapted from https://github.com/scriptcs/scriptcs/blob/dev/src/ScriptCs.Core/FilePreProcessor.cs
public class FileParser
{
private readonly string _workingDirectory;
private const string UsingString = "using ";
private const string LoadDirective = "#load";
private const string ReferenceDirective = "#r";
private readonly FileParserResult _result;

public FileParser(string workingDirectory)
Expand All @@ -23,6 +21,7 @@ public FileParserResult ProcessFile(string path)
ParseFile(path);
return _result;
}

private void ParseFile(string path)
{
var fullPath = Path.GetFullPath(path);
Expand All @@ -33,62 +32,35 @@ private void ParseFile(string path)

_result.LoadedScripts.Add(fullPath);

var scriptLines = File.ReadAllLines(fullPath).ToList();
foreach (var line in scriptLines)
{
ProcessLine(line);
}
}
var scriptCode = File.ReadAllText(fullPath);

public void ProcessLine(string line)
{
if (IsNamespaceLine(line))
var syntaxTree = CSharpSyntaxTree.ParseText(scriptCode, CSharpParseOptions.Default.
WithPreprocessorSymbols("load", "r").
WithKind(SourceCodeKind.Script).
WithLanguageVersion(LanguageVersion.Default));

var namespaces = syntaxTree.GetCompilationUnitRoot().Usings.Select(x => x.Name.ToString());
foreach (var ns in namespaces)
{
_result.Namespaces.Add(line.Trim(' ')
.Replace(UsingString, string.Empty)
.Replace("\"", string.Empty)
.Replace(";", string.Empty));
return;
_result.Namespaces.Add(ns.Trim());
}

if (IsDirectiveLine(line, LoadDirective))
var refs = syntaxTree.GetCompilationUnitRoot().GetReferenceDirectives().Select(x => x.File.ToString());
foreach (var reference in refs)
{
var filePath = GetDirectiveArgument(line, LoadDirective);
var fullPath = Path.IsPathRooted(filePath) ? filePath : Path.Combine(_workingDirectory, filePath);
if (!string.IsNullOrWhiteSpace(fullPath))
{
ParseFile(fullPath);
}
return;
_result.References.Add(reference.Replace("\"", string.Empty));
}

if (IsDirectiveLine(line, ReferenceDirective))
var loads = syntaxTree.GetCompilationUnitRoot().GetLoadDirectives().Select(x => x.File.ToString());
foreach (var load in loads)
{
var argument = GetDirectiveArgument(line, ReferenceDirective);
if (!string.IsNullOrWhiteSpace(argument))
var filePath = load.Replace("\"", string.Empty);
var loadFullPath = Path.IsPathRooted(filePath) ? filePath : Path.Combine(_workingDirectory, filePath);
if (!string.IsNullOrWhiteSpace(loadFullPath))
{
_result.References.Add(argument);
ParseFile(loadFullPath);
}

}
}

private static bool IsNamespaceLine(string line)
{
return line.Trim(' ').StartsWith(UsingString) && !line.Contains("{") && line.Contains(";") && !line.Contains("=");
}

private static bool IsDirectiveLine(string line, string directiveName)
{
return line.Trim(' ').StartsWith(directiveName);
}

private static string GetDirectiveArgument(string line, string directiveName)
{
return line.Replace(directiveName, string.Empty)
.Trim()
.Replace("\"", string.Empty)
.Replace(";", string.Empty);
}
}
}
6 changes: 1 addition & 5 deletions src/OmniSharp.Script/ScriptContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ public class ScriptContext
public Dictionary<string, List<PortableExecutableReference>> CsxReferences { get; } = new Dictionary<string, List<PortableExecutableReference>>();
public Dictionary<string, List<ProjectInfo>> CsxLoadReferences { get; } = new Dictionary<string, List<ProjectInfo>>();
public Dictionary<string, List<string>> CsxUsings { get; } = new Dictionary<string, List<string>>();

public HashSet<MetadataReference> CommonReferences { get; } = new HashSet<MetadataReference>
{
MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("mscorlib")).Location)
};
public HashSet<MetadataReference> CommonReferences { get; } = new HashSet<MetadataReference>();
public HashSet<string> CommonUsings { get; } = new HashSet<string> { "System" };
public string RootPath { get; set; }
}
Expand Down
51 changes: 38 additions & 13 deletions src/OmniSharp.Script/ScriptProjectSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using Microsoft.CodeAnalysis.Text;
using Microsoft.DotNet.InternalAbstractions;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -57,23 +58,47 @@ public void Initalize(IConfiguration configuration)
Context.RootPath = Env.Path;
Logger.LogInformation($"Found {allCsxFiles.Length} CSX files.");

var runtimeContext = ProjectContext.CreateContextForEachTarget(Env.Path).First();
Logger.LogInformation($"Found script runtime context for '{runtimeContext.ProjectFile.ProjectFilePath}'");
var runtimeContexts = ProjectContext.CreateContextForEachTarget(Env.Path);
var inheritedCompileLibraries = DependencyContext.Default.CompileLibraries.Where(x =>
x.Name.ToLowerInvariant().StartsWith("microsoft.codeanalysis") ||
x.Name.ToLowerInvariant().StartsWith("system.runtime"));

var projectExporter = runtimeContext.CreateExporter("Release");
var projectDependencies = projectExporter.GetDependencies();

foreach (var compilationAssembly in projectDependencies.SelectMany(x => x.CompilationAssemblies))
// if we have no context, then we also have no dependencies
// we can assume desktop framework
// and add mscorlib
if (runtimeContexts == null || !runtimeContexts.Any())
{
Logger.LogDebug("Discovered script compilation assembly reference: " + compilationAssembly.ResolvedPath);
Context.CommonReferences.Add(MetadataReference.CreateFromFile(compilationAssembly.ResolvedPath));
Logger.LogInformation("Unable to find project context for CSX files. Will default to non-context usage.");
Context.CommonReferences.Add(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location));
}
// otherwise we will grab dependencies for the script from the runtime context
else
{
// assume the first one
var runtimeContext = runtimeContexts.First();
Logger.LogInformation($"Found script runtime context '{runtimeContext?.TargetFramework.Framework}' for '{runtimeContext.ProjectFile.ProjectFilePath}'.");

var needsSystemRuntimeReference = Context.CommonReferences.Any(x => !x.Display.ToLowerInvariant().EndsWith("system.runtime.dll"));
var inheritedCompileLibraries = needsSystemRuntimeReference ?
DependencyContext.Default.CompileLibraries.Where(x => x.Name.ToLowerInvariant().StartsWith("microsoft.codeanalysis") || x.Name.ToLowerInvariant().StartsWith("system.runtime")) :
DependencyContext.Default.CompileLibraries.Where(x => x.Name.ToLowerInvariant().StartsWith("microsoft.codeanalysis"));
var projectExporter = runtimeContext.CreateExporter("Release");
var projectDependencies = projectExporter.GetDependencies();

// let's inject all compilation assemblies needed
var compilationAssemblies = projectDependencies.SelectMany(x => x.CompilationAssemblies);
foreach (var compilationAssembly in compilationAssemblies)
{
Logger.LogDebug("Discovered script compilation assembly reference: " + compilationAssembly.ResolvedPath);
Context.CommonReferences.Add(MetadataReference.CreateFromFile(compilationAssembly.ResolvedPath));
}

// if we are on .NET Core, we may need to avoid injecting System.Runtime
#if !NET46
var needsSystemRuntimeReference = Context.CommonReferences.Any(x => !x.Display.ToLowerInvariant().EndsWith("system.runtime.dll"));
inheritedCompileLibraries = needsSystemRuntimeReference ?
DependencyContext.Default.CompileLibraries.Where(x => x.Name.ToLowerInvariant().StartsWith("microsoft.codeanalysis") || x.Name.ToLowerInvariant().StartsWith("system.runtime")) :
DependencyContext.Default.CompileLibraries.Where(x => x.Name.ToLowerInvariant().StartsWith("microsoft.codeanalysis"));
#endif
}

// inject all inherited assemblies
foreach (var inheritedCompileLib in inheritedCompileLibraries.SelectMany(x => x.ResolveReferencePaths()))
{
Logger.LogDebug("Adding implicit reference: " + inheritedCompileLib);
Expand Down

0 comments on commit 696a902

Please sign in to comment.