How to create a ReSharper 8.X Custom Macro that can fetch and process the containing type name - resharper-8.0

ReSharper 8.X ships with a macro that fetches the "Containing Type Name", but what I want to do is manipulate that name. I'm using this in a Visual Studio 2013 Web API project, and I want a template that takes the class name and builds the URL that has to be called. So, for example, suppose I have this:
public class AnnouncementController : ApiController
{
//Want to put a template here!
[HttpGet]
public HttpResponseMessage GetActiveAnnouncements()
{
/// ...
}
}
now my ReSharper template will look something like this:
/// This sample shows how to call the <see cref="$METHOD$"/> method of controller $CLASS$ using the Web API.
/// https://myurl.mydomain.com/api/$CONTROLLER$/$METHOD$
$Controller$, by convention, is the class name minus the letters 'Controller'. This is because ASP.NET MVC Web API projects expect classes derived from ApiController to end with the string 'Controller',
Since this class is AnnouncementController, the template should output
https://myurl.mydomain.com/api/Announcement/GetActiveAnnouncements
Resharper's Built-In Macros can give me some of what I need, but I want to write a custom macro that fetches the containing type name and chops "Controller" off of it. I would like to do that directly, without storing the containing type name in another parameter.
Also, how do I install this custom macro? I've Googled around, and all I found was a lot of dead links and old walkthroughs written for ReSharper version 7 and below that do NOT work with ReSharper 8.x

After a lot of fighting, here is my solution.
[MacroImplementation(Definition = typeof (ControllerNameMacroDefinition))]
public class ControllerNameMacroImplementation : SimpleMacroImplementation
{
public ControllerNameMacroImplementation([Optional] IReadOnlyCollection<IMacroParameterValueNew> arguments)
{
}
public override HotspotItems GetLookupItems(IHotspotContext context)
{
var ret = "CONTROLLER";
var fileName = GetFileName(context);
if (!fileName.IsNullOrEmpty())
{
//Replace "Controller.cs" in two separate steps in case the extension is absent
ret = fileName.Replace("Controller", "").Replace(".cs", "");
}
return MacroUtil.SimpleEvaluateResult(ret);
}
/// <summary>
/// Returns the filename of the current hotspot context
/// </summary>
private string GetFileName(IHotspotContext context)
{
var psiSourceFile = context.ExpressionRange.Document.GetPsiSourceFile(context.SessionContext.Solution);
return psiSourceFile == null ? string.Empty : psiSourceFile.Name;
}
}

I wanted to do exactly this, but for JavaScript Jasmine tests -- SomethingViewModel.js, with a fixture of SomethingViewModelFixture.js, but wanted to be able to refer to SomethingViewModel in the file. A few slight modifications to the above made it possible.
Unfortunately, there's a ton more things you need to do in order to get your plugin to actually install. Here's a list. I hope it's comprehensive.
NuGet package install JetBrains.ReSharper.SDK, make sure you have the correct version installed!
Copy your Class Library DLL to C:\Users\<you>\AppData\Local\JetBrains\ReSharper\<version>\plugins\<your plugin name>, creating the plugins directory if needed.
You need the plugin Annotations in your AssemblyInfo.cs file:
[assembly: PluginTitle("Your extensions for ReSharper")]
[assembly: PluginDescription("Some description")] -- this is displayed in ReSharper->Options->Plugins
[assembly: PluginVendor("You")]
You need a class in your project that defines the MacroDefinition, as well as the above MacroImplementation
[MacroDefinition("MyNamespace.MyClassName", ShortDescription = "A short description of what it does.", LongDescription = "A long description of what it does.")]
"ShortDescription" - this is displayed in the "Choose Macro" dialog list.
"LongDescription" you'd think this would be in the "Choose Macro" description, but it isn't.
I just added this annotation to the above file.
The file you add the MacroDefinition to needs to implement IMacroDefinition, which has a method (GetPlaceholder) and a property (Parameters) on it. The former can return any string ("a") and the latter can return an empty array.
You can ignore the WiX/NuGet stuff if you want. Just for a local install.
In VS, the ReSharper->Options->Plugins section has some troubleshooting details on why your plugin might not be loading.
Good luck!

Related

How to access Custom field,which is defined in Construction feature package- Acumatica

By reading my question, you might think its very easy, but i request everyone to try to access a custom field defined in the construction feature package.
I want to access "Type" field in Project screen's Task Tab in details
UsrType is a custom field defined in Construction features package. In that package, file has been converted into dll. I tried to access that field like we usually do in customization.
but i got error
Type or Namespace "PMTaskExt" could not be found
I even tried this
I got error
UsrType Doesn't exist in PMTask
There is also same problem with UsrSubcontractNbr field in APTran. Not Only these fields, there are many such field to be accessed.
How can we access such fields?
From looking at PX.Objects.CN.dll it would be in the PX.Objects.CN.ProjectAccounting.PM.CacheExtensions namespace as PmTaskExt
Used the latest 19R2 Construction project "ConstructionFeatures_19_205_4_1_157"
Decompiled the customization dll (used DotPeek) I searched for PMTask:
Copied text:
using PX.Data;
using PX.Data.BQL;
using PX.Objects.CN.ProjectAccounting.PM.Descriptor;
using PX.Objects.CS;
using PX.Objects.PM;
namespace PX.Objects.CN.ProjectAccounting.PM.CacheExtensions
{
public sealed class PmTaskExt : PXCacheExtension<PMTask>
{
[PXDBString(30)]
[PXDefault]
[PXUIField(DisplayName = "Type", Required = true)]
[ProjectTaskType.List]
public string UsrType { get; set; }
public static bool IsActive()
{
return PXAccess.FeatureInstalled<FeaturesSet.construction>();
}
public abstract class usrType : BqlType<IBqlString, string>.Field<PmTaskExt.usrType>
{
}
}
}
Something like this should work:
var cnExt = PXCache<PX.Objects.PM.PMTask>.GetExtension<PX.Objects.CN.ProjectAccounting.PM.CacheExtensions.PmTaskExt>((PX.Objects.PM.PMTask)e.Row);
Do note the .Net version of PX.Objects.CN.dll is using 4.8 in case that causes any issues with version compatibility in visual studio if your solution is compiled on the same version of Acumatica for 19R2 which is 4.7.1

How can I know the retained class name or keyword when I use navigation framework in Android Studio?

The following code is from the project at https://github.com/mycwcgr/camera/tree/master/CameraXBasic
The project use the latest navigation framework, I find there are some retained class name such as CameraFragmentDirections, GalleryFragmentArgs.
The system have no prompt information for these class name, must I remember these keywords by myself?
Code
/** Method used to re-draw the camera UI controls, called every time configuration changes */
#SuppressLint("RestrictedApi")
private fun updateCameraUi() {
// Listener for button used to view last photo
controls.findViewById<ImageButton>(R.id.photo_view_button).setOnClickListener {
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
CameraFragmentDirections.actionCameraToGallery(outputDirectory.absolutePath))
}
}
/** Fragment used to present the user with a gallery of photos taken */
class GalleryFragment internal constructor() : Fragment() {
/** AndroidX navigation arguments */
private val args: GalleryFragmentArgs by navArgs()
}
No you do not need to remember these things by yourself, if you know of a trick.
For example, if you don't remember the "keyword" Directions, but you know you want to do something related to CameraFragment, you can start typing e.g. CameraFragm in Android Studio. It will then suggest CameraFragment and CameraFragmentDirections for you. That way you can find CameraFragmentDirections easily even though you did not remember the keyword Directions.
There are not that many keywords to worry about though. After working with the Navigation framework for a while, you will remember them all.
If you are curious, you can find the generated classes here after a build:
./app/build/generated/source/navigation-args/...
e.g. after a debug build:
./app/build/generated/source/navigation-args/debug/com/android/example/cameraxbasic/fragments/CameraFragmentDirections.java
If you are even more curious, the code that generates these classes is here:
https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/navigation/navigation-safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/java/JavaNavWriter.kt
There you can for example find this code:
internal fun Destination.toClassName(): ClassName {
val destName = name ?: throw IllegalStateException("Destination with actions must have name")
return ClassName.get(destName.packageName(), "${destName.simpleName()}Directions")
}
which is the code that decides what name CameraFragmentDirections gets. (Note "${destName.simpleName()}Directions" at the end.)

Implement missing members - add async when return type is Task?

This question relates to ReSharper. If I have an interface that looks like this:
public interface IOrder {
Task SetDeleted(Guid id);
}
and my class inherits from that interface, I would expect ReSharper to generate the following code, when selecting the "Implement missing members":
public class OrderService {
public async Task SetDeleted(Guid id) {
throw new NotImplementedException();
}
}
However, it completely ignores the async part of the method, so I have to type that manually every single time. This was fixed in 2016.3 of ReSharper, as described here (at the bottom).
However, it does not work for the CTRL + . keybinding (or whatever it is), that looks like this:
Is it possible to somehow change, how this behavior works within ReSharper? I want all generated Task methods to be async automatically. There is no option within ReSharper's "Members Generation" that enables me to do this.
In case class has only one missing member ReSharper doesn't show dialog therefore you can't tweak generation options. But you can add one more member to your interface and invoke generation action, this time ReSharper would show the dialog where you can set option "Make task-returning methods 'async'". This generation option is persistent i.e. it's last value will be stored in ReSharper settings and used by default.

Create file template with generic macro

this is my template code:
using System;
using System.Runtime.Serialization;
using Nethos.Ferramentas.AtributosValidacao.Numeros;
using Nethos.Ferramentas.AtributosValidacao.Textos;
$HEADER$namespace $NAMESPACE$
{
/// <summary>
/// Classe responsável pela persistência dos dados.
/// Tabela: $CLASS$s (PK: Id)
/// </summary>
[DataContract]
[KnownType(typeof(NHibernate.Collection.Generic.PersistentGenericBag< $CLASS$ >))]
[KnownType(typeof(NHibernate.Collection.PersistentBag))]
[Serializable]
public class $CLASS$
{
$END$
}
}
but in the line "[KnownType(typeof(NHibernate.Collection.Generic.PersistentGenericBag< $CLASS$ >))]" not appear name of the class, just the letter "a"... what's the problem with my code template?
I suspect this is an issue that the original 8.0 release of ReSharper has with certain plugins, under certain conditions. If you open Visual Studio by double clicking a solution file, the plugin would get initialised too late, and cause exceptions (you can verify by running "devenv.exe /ReSharper.Internal" and look for exceptions). The exceptions can interfere with various parts of ReSharper, unfortunately, macros is one that I've seen.
This has been fixed with ReSharper 8.0.1. Please can you update and try again?

Resharper (or Visual Studio) shortcut to cascade changes to a constructor

Here is a common refactoring that I don't believe I have seen a shortcut for:
Imagine we have a base class with a number of inheriting subclasses. The base classes uses constructor injection to accept a number of services:
class FooBase
{
private IMyService _myService;
private IMyOtherService _myOtherService;
public FooBase(IMyService myService, IMyOtherService _myOtherService)
{
_myService = myService;
_myOtherService = myOtherService;
}
}
class FooConcrete : FooBase
{
public FooConcrete(IMyService myService, IMyOtherService _myOtherService)
base(myService, myOtherService)
{
}
}
Notice how FooConcrete has to call the constructor of its base class, FooBase.
Now, what happens if I want to add another service to FooBase? I can quickly initialize the new private field from FooBase's constructor. But I still have to go around and manually update every subclass that inherits from FooBase. This can be a hassle if there are lots of inheriting classes.
Is there a shortcut, or perhaps a trick that I can use, to quickly update all the subclasses' constructors as well?
Since I asked this question Resharper have now added support as part of the Change Signature feature.
For example, to add a parameter to a constructor in the base class, use the following method:
Right click on the constructor you want to change, select Refactor->Change Signature.
Make the changes you require and click Next. Select "Resolve with call tree" and click Next.
Your change will be made, and a window will open somewhere in Visual Studio titled "Refactoring - Change signature". A list of inheriting classes will be shown in this window.
Double click on each of the classes and choose the "Create parameter x in constructor y"
Build, then Fix compilation errors.
ReSharper has no tool for this, including AFAIK in ReSharper 6, which got launched today.
Default Value in Change Signature
The best way I know is to add new service parameter to FooBase constructor by Change Signature (Ctrl+R,S) with a proper parameter name as default value, e.g. yetAnotherService.
After that you have indeed errors in base calls in all sub class constructors. But you can fix every error by only 3 key strokes:
- go to error by Alt+Shift+Page Down,
- open context menu by Alt+Enter,
- select first option (Create parameter 'yetAnotherService' in containing constructor) by Enter.
Ok you still have many key strokes. But it's the fastest way (with ReSharper 5.1.3) I know.
Parameter class by ReSharper refactoring
If you know that constructors can change through the lifetime you could create a parameter class like:
class FooInfo
{
IMyService MyService {get; set;}
IMyOtherService MyOtherService {get; set;}
}
and can pass such an object to constructor, i.e:
class FooBase
{
private FooInfo _fooInfo;
public FooBase(FooInfo fooInfo)
{
_fooInfo= fooInfo;
}
}
class FooConcrete : FooBase
{
public FooConcrete(FooInfo fooInfo)
base(fooInfo)
{
}
}
Then if you need a new parameter you just add it to FooInfo class. Changes in subclasses are not needed.
ReSharper has a refactoring for generating such a parameter class from constructor parameters:
Place cursor on constructor name in code file and
press Ctrl+Shift+R and
select Extract Class From Parameters....

Resources