Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object instance completions #616

Merged
merged 1 commit into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 102 additions & 102 deletions src/Microsoft.DotNet.Interactive.CSharp/InteractiveWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Reactive.Disposables;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Host.Mef;
Expand All @@ -11,180 +12,179 @@

namespace Microsoft.DotNet.Interactive.CSharp
{
internal class InteractiveWorkspace : IDisposable
internal class InteractiveWorkspace : Workspace
{
private ProjectId _previousSubmissionProjectId;
private ProjectId _currentSubmissionProjectId;
private readonly CompositeDisposable _disposables = new CompositeDisposable();
private int _submissionCount;
private readonly CSharpParseOptions _parseOptions;
private Compilation _currentCompilation;
private Solution _solution;
private TextContainer _committedTextContainer;
private DocumentId _committedDocumentId;
private DocumentId _workingDocumentId;

public InteractiveWorkspace()
public InteractiveWorkspace() : base(MefHostServices.DefaultHost, WorkspaceKind.Interactive)
{
var workspace = new AdhocWorkspace(MefHostServices.DefaultHost, WorkspaceKind.Interactive);

_committedTextContainer = new TextContainer();

_solution = workspace.CurrentSolution;

_parseOptions = new CSharpParseOptions(
LanguageVersion.Latest,
DocumentationMode.None,
SourceCodeKind.Script);

_disposables.Add(workspace);

_disposables.Add(Disposable.Create(() =>
{
_committedTextContainer = null;
_currentCompilation = null;
_solution = null;
}));
}

public void AddSubmission(ScriptState scriptState)
{
_currentCompilation = scriptState.Script.GetCompilation();

_previousSubmissionProjectId = _currentSubmissionProjectId;
var solution = CurrentSolution;
if (_currentSubmissionProjectId != null)
{
solution = solution.RemoveProject(_currentSubmissionProjectId);
}

_committedTextContainer.AppendText(scriptState.Script.Code);
SetCurrentSolution(solution);

var assemblyName = $"Submission#{_submissionCount++}";
var debugName = assemblyName;
_previousSubmissionProjectId = CreateProjectForPreviousSubmission(_currentCompilation, scriptState.Script.Code, _currentSubmissionProjectId, _previousSubmissionProjectId);

#if DEBUG
debugName += $": {scriptState.Script.Code}";
#endif
(_currentSubmissionProjectId, _workingDocumentId) = CreateProjectForCurrentSubmission(_currentCompilation, _previousSubmissionProjectId);
}

_currentSubmissionProjectId = ProjectId.CreateNewId(debugName: debugName);
private (ProjectId projectId, DocumentId workingDocumentId) CreateProjectForCurrentSubmission(Compilation previousCompilation, ProjectId projectReferenceProjectId)
{
var submission = _submissionCount++;
var solution = CurrentSolution;
var assemblyName = $"Submission#{submission}";
var compilationOptions = previousCompilation.Options.WithScriptClassName(assemblyName);
var debugName = assemblyName;
var projectId = ProjectId.CreateNewId(debugName: debugName);

var projectInfo = ProjectInfo.Create(
_currentSubmissionProjectId,
projectId,
VersionStamp.Create(),
name: debugName,
assemblyName: assemblyName,
language: LanguageNames.CSharp,
parseOptions: _parseOptions,
compilationOptions: _currentCompilation.Options,
metadataReferences: _currentCompilation.References);
compilationOptions: compilationOptions,
isSubmission: true);

solution = solution.AddProject(projectInfo);

_solution = _solution.AddProject(projectInfo);
if (projectReferenceProjectId != null)
{
solution = solution.AddProjectReference(
projectId,
new ProjectReference(projectReferenceProjectId)
);
}

var currentSubmissionDocumentId = DocumentId.CreateNewId(
_currentSubmissionProjectId,
debugName: debugName);

// add the code submission to the current project
var submissionSourceText = SourceText.From(scriptState.Script.Code);
var workingDocumentId = DocumentId.CreateNewId(
projectInfo.Id,
debugName: $"working document for {submission}");

_solution = _solution.AddDocument(
currentSubmissionDocumentId,
debugName,
submissionSourceText);
solution = solution.AddDocument(
workingDocumentId,
$"working document for {submission}",
string.Empty);

SetCurrentSolution(solution);
return (projectId, workingDocumentId);
}

if (_previousSubmissionProjectId != null)
private ProjectId CreateProjectForPreviousSubmission(Compilation compilation, string code, ProjectId projectId, ProjectId projectReferenceProjectId)
{
var solution = CurrentSolution;

var compilationOptions = compilation.Options;
var assemblyName = compilationOptions.ScriptClassName;
if (string.IsNullOrWhiteSpace(assemblyName))
{
_solution = _solution.AddProjectReference(
_currentSubmissionProjectId,
new ProjectReference(_previousSubmissionProjectId));
assemblyName = $"Submission#{_submissionCount}";
compilationOptions = compilationOptions.WithScriptClassName(assemblyName);
}

// remove rollup and working document from project

if (_committedDocumentId != null)
var debugName = assemblyName;
#if DEBUG
debugName += $": {code}";
#endif
if (projectId == null)
{
_solution = _solution.RemoveDocument(_committedDocumentId);
projectId = ProjectId.CreateNewId(debugName: debugName);
}

// create new ids and reuse buffers
var projectInfo = ProjectInfo.Create(
projectId,
VersionStamp.Create(),
name: debugName,
assemblyName: assemblyName,
language: LanguageNames.CSharp,
parseOptions: _parseOptions,
compilationOptions: compilationOptions,
metadataReferences: compilation.References,
isSubmission:true);

_committedTextContainer.AppendText(scriptState.Script.Code);

var currentSubmissionDocumentId = DocumentId.CreateNewId(
projectInfo.Id,
debugName: assemblyName);

var workingProjectName = $"Rollup through #{_submissionCount - 1}";
// add the code submission to the current project
var submissionSourceText = SourceText.From(code);

_committedDocumentId = DocumentId.CreateNewId(
_currentSubmissionProjectId,
workingProjectName);
solution = solution.AddProject(projectInfo);

if (projectReferenceProjectId != null)
{
solution = solution.AddProjectReference(
projectId,
new ProjectReference(projectReferenceProjectId)
);
}

_solution = _solution.AddDocument(
_committedDocumentId,
workingProjectName,
TextLoader.From(_committedTextContainer, new VersionStamp()));
solution = solution.AddDocument(
currentSubmissionDocumentId,
debugName,
submissionSourceText);

SetCurrentSolution(solution);
return projectId;
}


public Document ForkDocument(string code)
{
var solution = _solution;
var solution = CurrentSolution;
solution = solution.RemoveDocument(_workingDocumentId);

var workingDocumentName = $"Fork from #{_submissionCount - 1}";

var workingDocumentId = DocumentId.CreateNewId(
var workingDocumentName = $"Fork from #{_submissionCount}";
_workingDocumentId = DocumentId.CreateNewId(
_currentSubmissionProjectId,
workingDocumentName);

solution = solution.AddDocument(
workingDocumentId,
_workingDocumentId,
workingDocumentName,
SourceText.From(code)
);

var languageServicesDocument =
solution.GetDocument(workingDocumentId);

solution.GetDocument(_workingDocumentId);
SetCurrentSolution(solution);
return languageServicesDocument;

}

public void Dispose()
{
_disposables.Dispose();
}
}

internal class TextContainer : SourceTextContainer
{
private SourceText _currentText;

public TextContainer()
{
_currentText = SourceText.From(string.Empty);
}

public void SetText(string text)
protected override void Dispose(bool finalize)
{
var old = _currentText;
_currentText = SourceText.From(text);
if (TextChanged is { } e)
if (!finalize)
{
e.Invoke(this, new TextChangeEventArgs(old, _currentText));
_disposables.Dispose();
}
base.Dispose(finalize);
}

public void AppendText(string text)
{
var old = _currentText;
_currentText = _currentText.Replace(_currentText.Length,text.Length, text);
if (TextChanged is {} e)
{
e.Invoke(this, new TextChangeEventArgs(old, _currentText));
}
}

public override SourceText CurrentText => GetSourceText();

private SourceText GetSourceText()
{
return _currentText;
}

public override event EventHandler<TextChangeEventArgs> TextChanged;
}

}
Loading