Skip to content
This repository was archived by the owner on Oct 4, 2021. It is now read-only.

[Addin] To allow CBinding to use "Project" property #2870

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

gitexperience
Copy link
Contributor

CMakeProject being derived from SolutionItem is unable to access the "Project" property. The PR is intended to test the changes needed to make the "Project" property available to the CBinding's CMakeProject.

@monojenkins
Copy link
Contributor

Hello! I'm the build bot for the Mono project.

I need approval from a Mono team member to build this pull request. A team member should reply with "approve" to approve a build of this pull request, "whitelist" to whitelist this and all future pull requests from this contributor, or "build" to explicitly request a build, even if one has already been done. Additional trigger words are available here.

Contributors can ignore this message.

Copy link
Contributor

@mhutch mhutch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there! Just a few changes for consistency and backwards-compatibility.

@@ -56,7 +56,7 @@ public ConnectedServicesWidget ()
/// <summary>
/// Shows the services gallery and removes the details widget if it is visible
/// </summary>
public void ShowGallery(IConnectedService[] services, Project project)
public void ShowGallery(IConnectedService[] services, SolutionItem owner)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an overload that takes a Project and calls the SolutionItem version, so that the public API doesn't change.

@@ -88,6 +88,12 @@ protected override void OnSetProject (MonoDevelop.Projects.Project project)
content.Project = project;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to remove the OnSetProject as it's handled by OnSetOwner.

@@ -115,6 +115,12 @@ protected override void OnSetProject (Projects.Project project)
content.Project = project;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to remove the OnSetProject as it's handled by OnSetOwner.

@@ -38,9 +38,9 @@ public static GettingStartedProvider GetGettingStartedProvider (this Project pro
return null;
}

public static GettingStartedNode GetGettingStartedNode (this Project project)
public static GettingStartedNode GetGettingStartedNode (this SolutionItem owner)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a Project overload that chains to SolutionItem.

@@ -142,6 +143,20 @@ internal protected virtual void OnDeselected ()
protected virtual void OnSetProject (Project project)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be [Obsoleted]; subclasses should override Owner instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the Project property should chain to Owner, i.e.

public Project Project {
	get {
		return Owner as Project;
	}
	set {
		OnSetOwner(project);
	}
}


protected virtual void OnSetOwner (SolutionItem owner)
{
this.owner = owner;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also call OnSetProject() if the owner is a project.

if (owner is Project project)
{
    OnSetProject(project);
}

@@ -55,6 +55,7 @@ public sealed class ParseOptions
public ITextSource Content { get; set; }

public MonoDevelop.Projects.Project Project { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also forward to Owner.

@@ -92,7 +92,7 @@ public abstract class ViewContent : BaseViewContent
public virtual object GetDocumentObject ()
{
string path = IsUntitled ? UntitledName : ContentName;
if (IsFile && !string.IsNullOrEmpty (path) && Project != null) {
if (IsFile && !string.IsNullOrEmpty (path) && Owner != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be changed, since we use the Project property just below, so Project must be != null.

@slluis
Copy link
Member

slluis commented Aug 8, 2017

It looks good overall!

@gitexperience gitexperience changed the title [Addin] (Test PR) To allow CBinding to use "Project" property [Addin] To allow CBinding to use "Project" property Aug 8, 2017
@@ -524,6 +533,18 @@ internal Task<Document> OpenDocument (FilePath fileName, Project project, int li
return OpenDocument (openFileInfo);
}

internal Task<Document> OpenDocument (FilePath fileName, SolutionItem owner, int line, int column, OpenDocumentOptions options, Encoding encoding, IViewDisplayBinding binding)
{
var openFileInfo = new FileOpenInformation (fileName, owner as Project) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mhutch , here owner can only ever be Project .Is it ok to do so ? Or do we need to create all the referred methods for SolutionItem as well ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be propagated...

If you follow the logic -

  1. RealOpenFile reads the Project from the FileOpenInformation
    Project project = openFileInfo.Project ?? GetProjectContainingFile (fileName);
  2. The project gets passed to LoadFileWrapper
    var fw = new LoadFileWrapper (monitor, workbench, viewBinding, project, openFileInfo);
  3. it's used to set the Project on the ViewContent:

I would suggest adding an Owner property to FileOpenInformation and having FileOpenInformation.Project forward to that, just like the other owner/project forwarding pairs.

Then RealOpenFile and LoadFileWrapper can use the FileOpenInformation.Owner instead of the project, and sent the Owner on the ViewContent.

Fortunately RealOpenFile and LoadFileWrapper are not public so do not have to keep API compatibility.

@@ -9,6 +9,7 @@ type TestDocument(name, parsedDocument, editor: TextEditor) =

override x.Name = name
override x.Project = null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setting Project is unnecessary here as it's forwarded from Owner

@@ -524,6 +533,18 @@ internal Task<Document> OpenDocument (FilePath fileName, Project project, int li
return OpenDocument (openFileInfo);
}

internal Task<Document> OpenDocument (FilePath fileName, SolutionItem owner, int line, int column, OpenDocumentOptions options, Encoding encoding, IViewDisplayBinding binding)
{
var openFileInfo = new FileOpenInformation (fileName, owner as Project) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be propagated...

If you follow the logic -

  1. RealOpenFile reads the Project from the FileOpenInformation
    Project project = openFileInfo.Project ?? GetProjectContainingFile (fileName);
  2. The project gets passed to LoadFileWrapper
    var fw = new LoadFileWrapper (monitor, workbench, viewBinding, project, openFileInfo);
  3. it's used to set the Project on the ViewContent:

I would suggest adding an Owner property to FileOpenInformation and having FileOpenInformation.Project forward to that, just like the other owner/project forwarding pairs.

Then RealOpenFile and LoadFileWrapper can use the FileOpenInformation.Owner instead of the project, and sent the Owner on the ViewContent.

Fortunately RealOpenFile and LoadFileWrapper are not public so do not have to keep API compatibility.

Copy link
Contributor

@mhutch mhutch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple small changes but otherwise looking good

if (info.Project != null && doc.Project != info.Project) {
doc.SetProject (info.Project);
if (info.Owner != null && doc.Owner != info.Owner) {
doc.SetProject (info.Owner as Project);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be SetOwner :)

app.Launch (fileName);
}

Counters.OpenDocumentTimer.Trace ("Adding to recent files");
DesktopService.RecentFiles.AddFile (fileName, project);
DesktopService.RecentFiles.AddFile (fileName, owner as Project);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RecentFiles.Add could also have a SolutionItem owner overload.


if (openFileInfo.DisplayBinding != null) {
binding = viewBinding = openFileInfo.DisplayBinding;
} else {
var bindings = DisplayBindingService.GetDisplayBindings (fileName, null, project).ToList ();
var bindings = DisplayBindingService.GetDisplayBindings (fileName, null, owner as Project).ToList ();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add an overload to DisplayBindingService.GetDisplayBindings:

internal static IEnumerable<IDisplayBinding> GetDisplayBindings (FilePath filePath, string mimeType, SolutionItem owner)
{
    //FIXME: this cannot be yet implemented without breaking IDisplayBinding.CanHandle API.
    // it can be fixed when default interface methods are added to the C# language.
    // for now, just forward to the Project version.
    return GetDisplayBindings (filePath, mimeType, owner as Project);
}

That way there's a single place to fix it, when it's eventually possible to do so.

await fw.Invoke (fileName);
} else {
var extBinding = (IExternalDisplayBinding)binding;
var app = extBinding.GetApplication (fileName, null, project);
var app = extBinding.GetApplication (fileName, null, owner as Project);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as for IDisplayBinding.

@mhutch
Copy link
Contributor

mhutch commented Aug 21, 2017

build

public void ShowGallery(IConnectedService[] services, Project project)
public void ShowGallery (IConnectedService [] services, Project project)
{
ShowGallery (services, project as SolutionItem);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a hard cast here, i.e. (SolutionItem)project

The "hard" cast is a little clearer in intent. Generally, you should use a soft ("as") cast when you want to get null if the cast fails. In this case the cast cannot fail, as Project is derived from SolutionItem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, no cast is required here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore my comment.

@@ -212,6 +212,12 @@ public EmptyDocumentContext (Project project)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to override both Project and Owner, as they are chained. Overriding Owner is enough.

@@ -2394,7 +2394,7 @@ void CorrectIndenting ()
var formatter = CodeFormatterService.GetFormatter (Document.MimeType);
if (formatter == null || !formatter.SupportsCorrectingIndent)
return;
var policies = Project != null ? Project.Policies : null;
var policies = Owner != null ? Owner.Policies : null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW this can be simplified using C# 6 syntax to

var policies = Owner?.Null; 


public void AddFile (string fileName, SolutionItem owner)
{
var projectName = owner != null ? owner.Name : null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, this can be owner?.Name

@@ -40,7 +40,12 @@ public static GettingStartedProvider GetGettingStartedProvider (this Project pro

public static GettingStartedNode GetGettingStartedNode (this Project project)
{
return project.GetService<GettingStartedProjectExtension> ()?.ProjectPadNode;
return GetGettingStartedNode (project as SolutionItem);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard cast here too.

@@ -40,6 +40,7 @@ public abstract class BaseViewContent : IDisposable
{
IWorkbenchWindow workbenchWindow;
Project project;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this variable as it's no longer used.

}
set {
OnSetProject (value);
OnSetOwner (project);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct, the incoming value is value not project. Also, chaining would be cleaner than duplicating, i.e. Owner = value.

@@ -115,7 +123,7 @@ public static IEnumerable<FileViewer> GetFileViewers (FilePath filePath, Project
yield return new FileViewer (vb);
} else {
var eb = (IExternalDisplayBinding) b;
var app = eb.GetApplication (filePath, mimeType, ownerProject);
var app = eb.GetApplication (filePath, mimeType, ownerProject as SolutionItem);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hard cast here too

@@ -219,7 +219,7 @@ public object GetDocumentObject ()
Solution adhocSolution;

public override Project Project {
get { return (Window != null ? Window.ViewContent.Project : null) ?? adhocProject; }
get { return (Window != null ? Window.ViewContent.Owner as Project : null) ?? adhocProject; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should chain to Owner instead of duplicating it

}

void ListenToProjectLoad (Project project)
{
ListenToProjectLoad (project as SolutionItem);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard cast here too

@mhutch
Copy link
Contributor

mhutch commented Aug 21, 2017

@slluis - do you think it would make sense to use WorkspaceObject instead of SolutionItem for the type of Owner?

@@ -60,7 +60,7 @@ public interface IViewDisplayBinding : IDisplayBinding
///<summary>A display binding that opens an external application.</summary>
public interface IExternalDisplayBinding : IDisplayBinding
{
DesktopApplication GetApplication (FilePath fileName, string mimeType, Project ownerProject);
DesktopApplication GetApplication (FilePath fileName, string mimeType, SolutionItem owner);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an API break

@slluis
Copy link
Member

slluis commented Aug 22, 2017

@mhutch I think SolutionItem is ok.

@mhutch
Copy link
Contributor

mhutch commented Aug 22, 2017

@slluis I was just wondering about things like items in solution folders. It would be painful to have to change the type of "Owner" later.

@slluis
Copy link
Member

slluis commented Aug 23, 2017

@mhutch there isn't much functionality we can use at this abstraction level, it could as well be an Object. In any case solution folder items is a real use case, so I'm ok with using WorkspaceObject.

@mhutch
Copy link
Contributor

mhutch commented Aug 23, 2017

@gitexperience could you change SolutionItem to WorkspaceObject per above comment?

There may be some things that will not work because they access members that are not present on WorkspaceObject, but those can conditionally cast to the derived types that do have this functionality.

var policies = Owner?.Policies;

could become

var policies = (Owner as IPolicyProvider)?.Policies;

and

if (window.ViewContent.Owner != null)
    window.ViewContent.Owner.Modified += HandleProjectModified;

could become

if (window.ViewContent.Owner is SolutionFolderItem solutionItem)
    solutionItem.Modified += HandleProjectModified;
else if (window.ViewContent.Owner is WorkspaceItem workspaceItem)
    workspaceItem.Modified += HandleWorkspaceItemModified;

@mhutch
Copy link
Contributor

mhutch commented Aug 23, 2017

@gitexperience also, please rebase onto master

@@ -81,6 +81,7 @@ type FsiDocumentContext() =
override x.ReparseDocument() = ()
override x.GetOptionSet() = TypeSystemService.Workspace.Options
override x.Project = project :> Project
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is redundant, as you're setting Owner too

@@ -135,7 +136,13 @@ public override Microsoft.CodeAnalysis.Options.OptionSet GetOptionSet ()

public override MonoDevelop.Projects.Project Project {
get {
return originalContext.Project;
return originalContext.Owner as Project;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency/clarity of intent, this should chain to Owner rather than accessing originalContext directly.

@mhutch
Copy link
Contributor

mhutch commented Sep 5, 2017

Can you please rebase this onto master? The changes tab shows a lot of changes from master that appear to have been somehow cherry-picked.

@gitexperience gitexperience force-pushed the master branch 2 times, most recently from f2f76cd to 0dbd452 Compare September 5, 2017 07:01
@gitexperience
Copy link
Contributor Author

@mhutch, rebasing done!
Can you please review the changes ?

Copy link
Contributor

@mhutch mhutch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just one little issue remaining.

@@ -155,7 +156,7 @@ void UpdateStyleParent (MonoDevelop.Projects.Project styleParent, string mimeTyp
var mimeTypes = DesktopService.GetMimeTypeInheritanceChain (mimeType);

if (styleParent != null)
policyContainer = styleParent.Policies;
policyContainer = (styleParent as IPolicyProvider).Policies;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will throw a NRE if the styleParent does not implement IPolicyProvider.

I think this entire if/else could be simplified to

policyContainer = (styleParent as IPolicyProvider)
                  ?.Policies
                  ?? policyContainer = MonoDevelop.Projects.Policies.PolicyService.DefaultPolicies

@gitexperience
Copy link
Contributor Author

@mhutch , Done!

@gitexperience
Copy link
Contributor Author

@slluis @mhutch do we need more changes in this PR ?
I just rebased it again because of some merge conflicts.

@mhutch
Copy link
Contributor

mhutch commented Sep 28, 2017

It looks good to me but I'd like @slluis to take a look too

@@ -257,7 +257,14 @@ public override void DiscardChanges ()
protected override void OnSetProject (MonoDevelop.Projects.Project project)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not necessary to override OnSetProject if OnSetOwner is overriden.

if (window.ViewContent.Project != null) {
documentUrl = "file://" + window.ViewContent.Project.FileName;
if (window.ViewContent.Owner != null) {
documentUrl = "file://" + window.ViewContent.Owner.BaseDirectory.FileName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct, this will return the directory name, not the file name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@slluis, window.ViewContent.Project.FileName is working fine here so I left it as it is. Can you please review?

InitializeExtensionChain ();
ListenToProjectLoad (project);
ListenToProjectLoad (owner);
}

void ListenToProjectLoad (Project project)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not necessary.

@ghost ghost removed the cla-signed label Nov 8, 2017
@ghost ghost deleted a comment from msftclas Nov 8, 2017
@gitexperience gitexperience force-pushed the master branch 2 times, most recently from 345afe7 to ce82be8 Compare November 22, 2017 13:36
@slluis slluis added this to the 15.6 milestone Dec 5, 2017
@gitexperience
Copy link
Contributor Author

@slluis do we need more changes in this PR ?

@gitexperience gitexperience mentioned this pull request Jan 5, 2018
@slluis slluis removed this from the 15.6 milestone Mar 7, 2018
Base automatically changed from master to main March 9, 2021 14:17
@akoeplinger akoeplinger changed the base branch from main to master March 15, 2021 17:02
Base automatically changed from master to main March 15, 2021 17:03
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants