How do I write a very simple Visual Studio debugger visualizer? - c++

I'm trying to write an 'autoexp.dat'-based visualizer for a string type. I've scaled-back my ambitions to attempting to write a visualizer for a really simple test type that contains a null-terminated string field:
namespace thizz { namespace izz {
class MyType {
const char* _ptr;
public:
MyType(const char* ptr) : _ptr(ptr) {}
};
}
}
This is my stab at a visualiser, but it has no effect on how Visual Studio (2010) displays an instance of this type:
thizz::izz::MyType
{
preview ([$e._ptr,s])
}
(That's going at the top of the [Visualizers] section in C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\Packages\Debugger\autoexp.dat).
Watching an instance of this type:
thizz::izz::MyType t("testing testing");
Just displays
t | {_ptr=0x0f56a6fc "testing testing" } | thizz::izz::MyType
in the Watch window.

To get an even more versatile viewer try changing to use this:
thizz::izz::MyType {
preview ( #( [$e._ptr,s] ) )
stringview ( #( [$e._ptr,sb] ) )
}
this will also give the magnifying glass icon which will open a larger text view window in the case that you have a longer string. It'll also give you the option of rendering as HTML or XML.
Note that as well as the format of the file being sensitive to whitespace, I've also found that you can't use a colon in the string otherwise it generates parse errors.
The debugger visualisers are incredibly powerful, though the syntax can be quite bewildering. As general advice I would suggest creating some entries first in the [AutoExpand] section to summarise the data types that you are most interested in, and then if you have custom containers then copy and adapt the examples for vector, list, etc, which will give you the largest return for the investment in your time.

I can't give a categorical reason why my original 'code' in autoexp.dat was not working, but I found that the same code worked when all the whitespace was removed.
I then tried re-adding whitespace and found that keeping the initial open brace on the first line was necessary to keep the definition working.

Related

Hide/fold/dim arbitrary lines of code by regex (e.g. to hide logging)

There is a lot of logging in my C++ project. The logging is done via a log stream and the log lines have the following format:
APP_LOG_XXX() << ... ;
Those log lines blend with the rest of the code and make it harder to be read.
I want to somehow make these log lines appear in dimmed colors, or even better to hide/fold by a hotkey or click. There are a lot of log lines already, so wrapping them up in #pragma region would take much time (or would require writing a separate script). I wonder if there is an easier way.
(There is a very similar question on SO, but it's about Visual Studio, not Visual Studio Code).
You can use extension Highlight.
Set the color to a version close to your theme background color
Add to your settings.json
"highlight.regexes": {
"(APP_LOG_XXX\\(\\) <<[^;]+;)": {
"regexFlags": "mg",
"decorations": [
{ "color": "#f0f0f0" }
]
}
}
Or you can use the opacity decoration property instead. The following configuration will dim the text while preserving its current syntax highlighting:
"highlight.regexes": {
"(APP_LOG_XXX\\(\\) <<[^;]+;)": {
"regexFlags": "mg",
"decorations": [
{ "opacity": "0.4" }
]
}
}

VSIX how to get current snapshot document name?

I have been trying to to create an extension that highlights specific line numbers for me in Visual Studio in the margins.
I manged to get my marking in the margins using predefined line number but for it to work properly I need to know what the current document FullName is (Path and filename)
After much googling I figured out how to do it with the sample code (which is not ideal)
DTE2 dte = (DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.15.0");
var activeDocument = dte.ActiveDocument;
var docName = activeDocument.Name;
var docFullName = activeDocument.FullName;
Now I know the problems here
is that is for specific version bases on the text
there is no way to select which instance (when running more than one VS)
It seems to be very slow
I have a feeling I should be doing this with MEF Attributes but the MS docs examples are so simple that they do not work for me. I scanned a few SO questions too and I just cannot get them to work. They mostly talk about Services.. which I do not have and have no idea how to get.
The rest of my code uses SnapshotSpans as in the example Extension of Todo_Classification examples which is great if you do NOT need to know the file name.
I have never done any extensions development. Please can somebody help me do this correctly.
You can use following code to get a file from a snapshot without any dependencies.
public string GetDocumentPath(Microsoft.VisualStudio.Text.ITextSnapshot ts)
{
Microsoft.VisualStudio.Text.ITextDocument textDoc;
bool rc = ts.TextBuffer.Properties.TryGetProperty(
typeof(Microsoft.VisualStudio.Text.ITextDocument), out textDoc);
if (rc && textDoc != null)
return textDoc.FilePath;
return null;
}
If you don't mind adding Microsoft.CodeAnalysis.EditorFeatures.Text to your project it will provide you with an extension method Document GetOpenDocumentInCurrentContextWithChanges() on the Microsoft.VisualStudio.Text.Snapshot class. (Plus many other Rosyln based helpers)
using Microsoft.CodeAnalysis.Text;
Document doc = span.Snapshot.GetOpenDocumentInCurrentContextWithChanges();

debug visualizer of C++ custom types for Visual Studio

I am working with a library with some awkward types. When debugging in Visual Studio, I would like to display them in a readable form. I found some really useful articles about how to edit autoexp.dat file.
http://www.idigitalhouse.com/Blog/?p=83
or
http://mariusbancila.ro/blog/2007/04/06/tweaking-autoexpdat-for-custom-types-in-vs2005/
Suppose I have a String class:
class String {
//...
private:
char *_cbuf;
}
then I can add the visualizer easily because _cbuf is a member variable. I just write
String{
preview (
[$c._cbuf]
)
}
at the beginning of [Visualizer] section in autoexp.dat file and it works.
But suppose that I want to display a more complex type which does not have any useful member variables but it has very useful methods. E.g.:
class Date {
//...
String asString() const;
private:
long _someReallyStrangeAndUnusefulDateRepresentation;
}
And I want to display the string rather than the unuseful long. How to do that? Writing
Date{
preview (
[$c.asString()]
)
}
in autoexp.dat does not work.
OK, after some research it seems it is generally possible but not directly by just editing of autoexp.dat.
First solution is to use EEAddIn.dll as described here: http://msdn.microsoft.com/en-us/library/8fwk67y3%28v=VS.90%29.aspx
another solution might be using inlined function as in Lucien Murray-Pitts' comment down on this page: http://www.virtualdub.org/blog/pivot/entry.php?id=120 which is inspired by boost debug visualizers.
However I have not yet tried any of these.

Can I programmatically collapse/expand all preprocessor blocks of a certain name in Visual Studio 2012?

My current project has a lot of debug preprocessor blocks scattered throughout the code. These are intentionally named differently to the system _DEBUG and NDEBUG macros, so I have a lot of this:
// Some code here
#ifdef PROJNAME_DEBUG
//unit tests, assumption testing, etc.
#endif
// code continues
These blocks sometimes get rather large, and their presence can sometimes inhibit code readability. In Visual Studio 2012 I can easily collapse these, but it would be nice to automatically have all of them collapsed, allowing me to expand them if I want to see what's in there. However, as I also have a bunch of header guards I don't want to collapse all preprocessor blocks, only the #ifdef PROJNAME_DEBUG ones.
Can I do this?
This is the most easiest scenario you can achive it, I think.
You should create an Add-In first in C#. (in VS 2013 they become deprecated :( )
In the OnConnection method you should add your command:
public void OnConnection( object application, ext_ConnectMode connectMode, object addInInst, ref Array custom )
{
_applicationObject = (DTE2)application;
if (connectMode == ext_ConnectMode.ext_cm_AfterStartup || connectMode == ext_ConnectMode.ext_cm_Startup)
{
Commands2 commands = (Commands2)_applicationObject.Commands;
try
{
//Add a command to the Commands collection:
Command command = commands.AddNamedCommand2(_addInInstance, "MyAddinMenuBar", "MyAddinMenuBar", "Executes the command for MyAddinMenuBar", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
}
catch (System.ArgumentException)
{
//If we are here, bla, bla... (Auto generated)
}
}
}
Note: you can find how parameters are act at the reference of AddNamedCommand2
The template created version would be also fine, but naturaly it worth to name your command properly.
After that you need to add your logic to Exec method:
public void Exec( string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled )
{
handled = false;
if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if (commandName == "MyAddinMenuBar.Connect.MyAddinMenuBar")
{
List<string> args = (varIn as string).Split(' ').ToList();
TextSelection ts;
ts = (TextSelection)_applicationObject.ActiveDocument.Selection;
EditPoint ep = (ts.ActivePoint).CreateEditPoint();
ep.StartOfDocument();
do
{
string actualLine = ep.GetLines(ep.Line, ep.Line + 1);
if (args.TrueForAll(filter => actualLine.Contains(filter)))
{
_applicationObject.ExecuteCommand("Edit.GoTo", ep.Line.ToString());
_applicationObject.ExecuteCommand("Edit.ToggleOutliningExpansion");
}
ep.LineDown();
} while (!ep.AtEndOfDocument);
handled = true;
return;
}
}
}
Note: Name you given to the command is checked in exec.
Than you can build.
Deployment of Add-In can happen through an [ProjectName].AddIn file in ..\Documents\Visaul Studio 20[XY]\AddIns\. (Created by the template, you should copy if you move the Add-In elsewhere)
You should place your Add-In assembly where the Assembly element of the mentioned file you set to point. To change version you should modify the text in Version element.
After you deployed and started Studio, you should activate the Add-In in the manager in Toolsmenu.
You need to expand all collapsable section in your code file (CTRL+M+L with C# IDE settigs).
This is required because I found only a way to invert the state of collapsion. If you find better command, you can change it.
Next you should activate Command Window to use the the created command.
Now only you need to type your commands name, like this:
MyAddinMenuBar.Connect.MyAddinMenuBar #ifdef PROJNAME_DEBUG
Hopefully magic will happen.
This solution is independent of language of code you edit so pretty multifunctional.

Is there any way to make Visual Studio stop indenting namespaces?

Visual Studio keeps trying to indent the code inside namespaces.
For example:
namespace Foo
{
void Bar();
void Bar()
{
}
}
Now, if I un-indent it manually then it stays that way. But unfortunately if I add something right before void Bar(); - such as a comment - VS will keep trying to indent it.
This is so annoying that basically because of this only reason I almost never use namespaces in C++. I can't understand why it tries to indent them (what's the point in indenting 1 or even 5 tabs the whole file?), or how to make it stop.
Is there a way to stop this behavior? A config option, an add-in, a registry setting, hell even a hack that modifies devenv.exe directly.
As KindDragon points out, Visual Studio 2013 Update 2 has an option to stop indenting.
You can uncheck TOOLS -> Options -> Text Editor -> C/C++ -> Formatting -> Indentation -> Indent namespace contents.
Just don't insert anything before the first line of code. You could try the following approach to insert a null line of code (it seems to work in VS2005):
namespace foo
{; // !<---
void Test();
}
This seems to suppress the indentation, but compilers may issue warnings and code reviewers/maintainers may be surprised! (And quite rightly, in the usual case!)
Probably not what you wanted to hear, but a lot of people work around this by using macros:
#define BEGIN_NAMESPACE(x) namespace x {
#define END_NAMESPACE }
Sounds dumb, but you'd be surprised how many system headers use this. (glibc's stl implentation, for instance, has _GLIBCXX_BEGIN_NAMESPACE() for this.)
I actually prefer this way, because I always tend to cringe when I see un-indented lines following a {. That's just me though.
Here is a macro that could help you. It will remove indentation if it detects that you are currently creating a namespace. It is not perfect but seems to work so far.
Public Sub aftekeypress(ByVal key As String, ByVal sel As TextSelection, ByVal completion As Boolean) _
Handles TextDocumentKeyPressEvents.AfterKeyPress
If (Not completion And key = vbCr) Then
'Only perform this if we are using smart indent
If DTE.Properties("TextEditor", "C/C++").Item("IndentStyle").Value = 2 Then
Dim textDocument As TextDocument = DTE.ActiveDocument.Object("TextDocument")
Dim startPoint As EditPoint = sel.ActivePoint.CreateEditPoint()
Dim matchPoint As EditPoint = sel.ActivePoint.CreateEditPoint()
Dim findOptions As Integer = vsFindOptions.vsFindOptionsMatchCase + vsFindOptions.vsFindOptionsMatchWholeWord + vsFindOptions.vsFindOptionsBackwards
If startPoint.FindPattern("namespace", findOptions, matchPoint) Then
Dim lines = matchPoint.GetLines(matchPoint.Line, sel.ActivePoint.Line)
' Make sure we are still in the namespace {} but nothing has been typed
If System.Text.RegularExpressions.Regex.IsMatch(lines, "^[\s]*(namespace[\s\w]+)?[\s\{]+$") Then
sel.Unindent()
End If
End If
End If
End If
End Sub
Since it is running all the time, you need to make sure you are installing the macro inside in your EnvironmentEvents project item inside MyMacros. You can only access this module in the Macro Explorer (Tools->Macros->Macro Explorer).
One note, it does not currently support "packed" namespaces such as
namespace A { namespace B {
...
}
}
EDIT
To support "packed" namespaces such as the example above and/or support comments after the namespace, such as namespace A { /* Example */, you can try to use the following line instead:
If System.Text.RegularExpressions.Regex.IsMatch(lines, "^[\s]*(namespace.+)?[\s\{]+$") Then
I haven't had the chance to test it a lot yet, but it seems to be working.
You could also forward declare your types (or whatever) inside the namespace then implement outside like this:
namespace test {
class MyClass;
}
class test::MyClass {
//...
};
Visual Studio 2017+
You can get to this "Indent namespace contents" setting under Tools->Options then Text Editor->C/C++->Formatting->Indention. It's deep in the menus but extremely helpful once found.
I understand the problem when there are nested namespaces. I used to pack all the namespaces in a single line to avoid the multiple indentation. It will leave one level, but that's not as bad as many levels. It's been so long since I have used VS that I hardly remember those days.
namespace outer { namespace middle { namespace inner {
void Test();
.....
}}}