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

Codist Beta 7.6 #283

Closed
26 tasks done
wmjordan opened this issue Jun 17, 2023 · 48 comments
Closed
26 tasks done

Codist Beta 7.6 #283

wmjordan opened this issue Jun 17, 2023 · 48 comments
Assignees
Labels
💾 have download

Comments

@wmjordan
Copy link
Owner

wmjordan commented Jun 17, 2023

Download

Codist 9081

Codist 9076
Codist 9058
Codist 9056
Codist 9020
Codist 9015
Codist 8980
Codist 8973
Codist 8926
Codist 8918

What's New

@fitdev
Copy link

fitdev commented Jun 29, 2023

Small suggestion: Is it possible to add a variant of Find Derived Classes / Interfaces, where only the classes / interfaces that directly inherit are shown (not the entire chain, i.e. no grand-children, no great-grand-children, etc.)?

@wmjordan
Copy link
Owner Author

There's a new beta version which implements limited derivation search.
Press down Ctrl key before executing Find Derived Classes or Find Inherited Interfaces command.

@fitdev
Copy link

fitdev commented Jun 29, 2023

That's awesome! You are so fast! Thank you!

@fitdev
Copy link

fitdev commented Jun 29, 2023

Just tried latest beta. Does not seem to work for me - the list is always empty, even though there are several directly-derived classes in other assemblies (some abstract, some not). Interfaces seem to work though.

@wmjordan
Copy link
Owner Author

It works for me. Can you make a sample solution?

image

Left: Directly derived
Right: All derived

@fitdev
Copy link

fitdev commented Jun 29, 2023

The issue is when those derived classes are in other assemblies / projects than the base class. If the two classes share the same assembly, then it works just fine.

@wmjordan
Copy link
Owner Author

Please try the new beta.

@fitdev
Copy link

fitdev commented Jun 29, 2023

Still the same issue. If a derived class is in the same assembly, it shows up. If it is in different assembly, it does not show up. If however I search for all derived classes (previous default behavior) - they all show up.

@fitdev
Copy link

fitdev commented Jun 29, 2023

Perhaps you could just use filtering down approach - i.e. use the code that works - that lists all derived classes, but then check each candidate manually to make sure it actually is directly-derived. That way none of the directly derived classes will be missed.

@wmjordan
Copy link
Owner Author

It had something to do with generic classes.

OK, please download the new beta.

@fitdev
Copy link

fitdev commented Jun 30, 2023

Unfortunately it still does not work. And in my case there are no generic classes involved. Unfortunately as of yet I could not produce a repro that I can send, because it does work in the test solution I made with 2 projects.

So, perhaps you can just reuse the old code that lists all derived classes / interfaces, and then manually check each one if it is a directly-derived, and if it is keep it in the list (as opposed to trying to get only directly derived classes only in the first place).

@wmjordan
Copy link
Owner Author

Yes, it is what Codist is doing--find all derived classes first and filter them, keeping those which have type.BaseType = questionedType.

I have a solution that has multiple projects and the filter is doing its job without any problem.
The base type is in project A and some directly derived types in project A, some in project B and C.

@fitdev
Copy link

fitdev commented Jun 30, 2023

In other words, perhaps you can reuse

var classes = (await SymbolFinder.FindDerivedClassesAsync(symbol as INamedTypeSymbol, context.Document.Project.Solution).ConfigureAwait(false)).ToList();

And then manually check each item in classes that it is directly-derived.

@fitdev
Copy link

fitdev commented Jun 30, 2023

Yes, it is what Codist is doing--find all derived classes first and filter them, keeping those which have type.BaseType = questionedType.

Oh I see. Very strange then. What filtering code are you using to get at directly-derived classes? Can you share your code?

@wmjordan
Copy link
Owner Author

internal static async Task FindDerivedClassesAsync(this SemanticContext context, ISymbol symbol, bool directDerive) {
	var type = symbol as INamedTypeSymbol;
	var classes = await SymbolFinder.FindDerivedClassesAsync(type, context.Document.Project.Solution).ConfigureAwait(false);
	if (directDerive) {
		if (type.IsGenericType) {
			classes = classes.Where(t => {
				var bt = t.BaseType;
				return bt.Equals(type) || bt.IsGenericType && bt.OriginalDefinition.Equals(type);
			});
		}
		else {
			classes = classes.Where(t => t.BaseType.Equals(type));
		}
	}
	await SyncHelper.SwitchToMainThreadAsync(default);
	ShowSymbolMenuForResult(symbol, context, classes.ToList(), directDerive ? R.T_DirectlyDerivedClasses : R.T_DerivedClasses, false);
}

@fitdev
Copy link

fitdev commented Jun 30, 2023

Thank you for sharing the code. I guess it maybe something to do with Roslyn. I will get back to you if I get some more ideas.

@fitdev
Copy link

fitdev commented Jun 30, 2023

Could you perhaps add some logging into this routine that will record fully qualified name of: symbol, and then all the classes (their fully qualified names, and their BaseType fully qualified names)/ Maybe save this log in Codist's default settings location where Codists's settings are stored?

@wmjordan
Copy link
Owner Author

Somehow the BaseType.Equals(type) fails. The "qualified type names", I guess, won't help too much.

@fitdev
Copy link

fitdev commented Jun 30, 2023

That's what I thought. Which is why I thought it would be nice to see the actual lists that Roslyn gives for each case, including their base types. Just to see what we are comparing with what. Perhaps instead of qualified names, record location / project / assembly of the symbol in question?

BTW, this may also shed some light into other Roslyn-related / find-symbol-related areas as well - as to why they may fail too.

@fitdev
Copy link

fitdev commented Jun 30, 2023

I mean the idea is to have a text representation of each BaseType (its name, assembly / project, or whatever else uniquely identifies it) and type to see why Equals fails. Since you cannot repro this, the only way is for me to run this version on my end and see what the logs will display. Ideally logging of all classes would be good to include too.

@fitdev
Copy link

fitdev commented Jun 30, 2023

BTW: Do the latest beta version of Codist re-introduce parenthesis/quotes automatic surrounding of selected text issue? I noticed that my selection gets surrounded with "s when I type over it. This started happening once I updated Codist version. But I could find nothing in the options.

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 1, 2023

Do the latest beta version of Codist re-introduce parenthesis/quotes automatic surrounding of selected text issue?

Yes. It does. A few users asked for that.
I change the way it works and I am working with that feature turned on for quite some time now.
The previous implementation was somewhat trouble-making, but I seldom feel annoyed by the new implementation now.
Give it a try :)

@fitdev
Copy link

fitdev commented Jul 1, 2023

Yes. It does. A few users asked for that.

Oh I see. Please make it optional. Right now it is very annoying - I certainly do not want any smart behavior with selections - just plain overwrite. Perhaps add a new checkbox under Feature Controllers on the General Page. Or introduce a whole new options page with typing/editor tweaks?

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 1, 2023

Right now it is very annoying.

Once the first character is typed,

  1. if no more characters are to be inserted after it, you can press Delete or Backspace key to remove the content.
  2. if more characters are to be inserted after the first character, just keep typing, the not-yet-removed content will be replaced.
  3. the automatically inserted paired character is what we need, that is fine.

On my side, situation 2 + situation 3 is far more frequent than situation 1. Thus I can tolerate situation 1.

Anyway, an option will be there in the next version.

@fitdev
Copy link

fitdev commented Jul 1, 2023

Thank you for explanation. I will give it a try. But glad to know that there will also be an option!

Does the new beta you just released contain any logging with regards to FindDerivedClassesAsync? I am really curious to see what the results are for my solution and why it may fail on Equals.

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 1, 2023

The previous implementation did not allow the above situation 2, and situation 1 required moving the caret, thus it was very very very annoying 😡 .
The new implementation tweaked the selection after auto-surrounding character pairs, and I found that I began to take advantage of the auto-surround behavior (situation 3) recently. Thus I decided to include it into the beta and let it receive some feedback.

That feature will by default turned off in the finally released version unless I found some better way to introduce the changes in the new version to the users.

Does the new beta you just released contain any logging with regards to FindDerivedClassesAsync?

No. Not yet. The log will have to be quite verbose I guess. I need to figure out what to be logged.

@fitdev
Copy link

fitdev commented Jul 1, 2023

I need to figure out what to be logged.

Ideally assembly / project name, file location (at least first code file in case of partials), and symbol name (though name + namespace would be even better).

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 1, 2023

@fitdev

Please try this beta, which generates some logs.
Codist.logged.9057.zip

To activate the log and make Codist log to the log file:

  1. Close VS.
  2. Open the config.json file with a text editor,
  3. Add "LogPath": "D:\\log.txt", into it, like the following code snippet shows. The log path can be any place that you can access.
  4. Save the config.json file.
  5. Open VS.
  6. There might be something already logged into the log file.
  7. Delete the log content in the file and save it, since the content there is not of your interest yet.
  8. Execute the "Ctrl"ed Find Derived Classes command.
  9. Examine the log file. It should have some entries with != in it.
{
  "Version": "7.5.0",
  "LogPath": "D:\\log.txt",
  "DisplayOptimizations": "HideSearchBox, HideAccountBox, ShowCpu",
// reset omitted ...

@fitdev
Copy link

fitdev commented Jul 1, 2023

Thanks so much. I will try it soon and let you know my findings!

@fitdev
Copy link

fitdev commented Jul 1, 2023

Just tried it. Unfortunately VS crashes when I attempted Ctrl+FindDerived:

Application: devenv.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.NullReferenceException
   at Codist.Controls.SymbolCommands+<>c__DisplayClass1_0.<FindDerivedClassesAsync>b__1(Microsoft.CodeAnalysis.INamedTypeSymbol)
   at System.Linq.Enumerable+WhereEnumerableIterator`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].MoveNext()
   at System.Collections.Generic.List`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]..ctor(System.Collections.Generic.IEnumerable`1<System.__Canon>)
   at System.Linq.Enumerable.ToList[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](System.Collections.Generic.IEnumerable`1<System.__Canon>)
   at Codist.Controls.SymbolCommands+<FindDerivedClassesAsync>d__1.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
   at Codist.Controls.CSharpSymbolContextMenu+UIHost+<FindDerivedClasses>d__33.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
   at MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Int32)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)

The log file was empty.

@fitdev
Copy link

fitdev commented Jul 1, 2023

The new implementation tweaked the selection after auto-surrounding character pairs, and I found that I began to take advantage of the auto-surround behavior (situation 3) recently. Thus I decided to include it into the beta and let it receive some feedback.

Yes, the new approach is quite smart! I like how you handled the common cases. But what if I want to type just one of the parenthesis replacing selection. Presumably I would have to hit Delete key twice? If that is the case, then I suggest that rule 1 is altered such that it will use default (overwrite) behavior - removing not only the selected text but also the 2nd parenthesis - which should occur automatically after either Delete or Backspace key was pressed the first time.

@fitdev
Copy link

fitdev commented Jul 1, 2023

Also, got another VS crash (unrelated to logging or Derived Classes thing) - apparently when it tried to display Super Quick Info:

Application: devenv.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.NullReferenceException
   at Codist.QuickInfo.QuickInfoOverrideController+QuickInfoPositioner.Reposition(Boolean)
   at Codist.QuickInfo.QuickInfoOverrideController+QuickInfoPositioner.QuickInfo_SizeChanged(System.Object, System.Windows.SizeChangedEventArgs)
   at System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(System.Object, System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject, System.Windows.RoutedEventArgs)
   at System.Windows.FrameworkElement.OnRenderSizeChanged(System.Windows.SizeChangedInfo)
   at System.Windows.ContextLayoutManager.fireSizeChangedEvents()
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.ContextLayoutManager.UpdateLayoutCallback(System.Object)
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(System.Object)
   at System.Windows.Media.MediaContext.RenderMessageHandler(System.Object)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
   at MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Int32)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 1, 2023

I checked out the source code of Roslyn and found that there was no magic.
It always use type.BaseType?.OriginalDefinition.Equals alike method to compute the matched base type.

Please try the new beta and see whether it works.

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 2, 2023

what if I want to type just one of the parenthesis replacing selection. Presumably I would have to hit Delete key twice?

No. You don't need hitting Delete twice.
Select the content to be replaced. Type (, then hit Delete. The previous selected content and the automatically inserted ) will be erased together.

Do NOT press Delete before typing (.
Otherwise, there will be an empty selection before typing ( and Codist won't auto-surround the content.
When you type (. It is now VS which automatically inserts the ) for you.
And you have to hit Delete once again.

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 2, 2023

There's another approach which displays class inheritance in a hierarchical view.

A quick and dirty implementation looks like the following:
image

@fitdev
Copy link

fitdev commented Jul 2, 2023

There's another approach which displays class inheritance in a hierarchical view.

This is awesome! I really like it!

@fitdev
Copy link

fitdev commented Jul 2, 2023

Tried the new beta. Directly-Derived Classes is empty once again. And log file is empty too. I guess this version has no logging. It would be nice to see the log, so can you please enable logging for this routine?

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 2, 2023

In short, the log would not help since the underlying conditions are complicated.

I reproduced the problem of BaseType comparison while I was developing the hierarchical result presentation.
In one test, I tried to find directly-derived classes for System.Attribute. In the temporary results, I observed at least 3 System.Attribute instances with the same looking but actually different containing assemblies (mscorlib).

Then I checked more thoroughly into the source code of Roslyn and found that special type comparison mechanism was used while searching for derived classes.
That mechanism was not publicly exposed via API. Thus we have to recreate our own wheel.

@fitdev
Copy link

fitdev commented Jul 2, 2023

Glad you were able to at least reproduce the issue on your end, as I could not produce a repro - it only occurs in my large solution. Hopefully the workaround won't be too difficult.

Though I do think that perhaps having some kind of logging mechanism for Roslyn-related stuff in Codist could be useful potentially to diagnose other kinds of issues, like the one from a few months back where symbols could not be navigated to in the NaviBar.

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 2, 2023

symbols could not be navigated to in the NaviBar

While I am working on prerequisites for #231, this issue has been partially addressed in the previous beta and will get better dealt with in upcoming versions.

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 3, 2023

The new beta should be able to fix the missing classes in Find Derived Classes command.
Also, pressing Shift when executing the command will list the derived classes in hierarchical order.

A new button is added to copy the symbol list content to clipboard.

The potential crash in Quick Info repositioning is also addressed.

@fitdev
Copy link

fitdev commented Jul 3, 2023

The new beta works great! Thank you so much for all your work! I really like the new hierarchical display of derived classes too!

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 3, 2023

I really like the new hierarchical display of derived classes too!

Please help test the new hierarchical view by comparing its output with the one from the previous implementation.
If the number of the new view and the previous view equals, it probably works correctly. Otherwise, there might be something wrong with the new view.
You can use the Copy List Content button at the top right corner of the window to copy the lists from bother views into the clipboard and use a text editor to unindent and sort the output, then use a diff tool, for instance, WinMerge, to compare the output from the new implementation against the original implementation.

@fitdev
Copy link

fitdev commented Jul 3, 2023

Please help test the new hierarchical view by comparing its output with the one from the previous implementation.

I will keep an eye on this. So far the counts have matched, so it works really well. I will let you know if I come across inconsistencies.

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 5, 2023

@fitdev
Please download and help test the new beta.

I am going to release this version.

  1. The Auto Pair Punctuation feature should be off by default.
  2. The hierarchical view for Find Derived Classes will be the default view (Shift: revert to old behavior).

@fitdev
Copy link

fitdev commented Jul 5, 2023

Both items work as they should! Really like the new derived classes representation!

@wmjordan
Copy link
Owner Author

wmjordan commented Jul 6, 2023

Really like the new derived classes representation!

Maybe there will be a tree view in the future version, so we can collapse and expand hierarchical items.

@fitdev
Copy link

fitdev commented Jul 6, 2023

That would be great or a multi-column list-view like control! I think though in general, (perhaps I even suggested it sometime ago in a separate issue), it would be nice to have improved sorting/filtering capability for the symbol list control (including the one used for NaviBar). Like ability to filter not just accessibility levels but kinds of classes - static, record, sealed, etc; kinds of members - extension, virtual, abstract, etc.

@wmjordan wmjordan closed this as completed Jul 7, 2023
@wmjordan wmjordan unpinned this issue Jul 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💾 have download
Projects
None yet
Development

No branches or pull requests

2 participants