Edit current visual studio document/file without write/save - c#

I am working on a visual studio extension where I have to edit the current code open in the code pane.
Here is how my file edit code goes:
DTE dTE = Package.GetGlobalService(typeof(DTE)) as DTE;
TextDocument activeDoc = dTE.ActiveDocument.Object() as TextDocument;
string text =
activeDoc.CreateEditPoint(activeDoc.StartPoint).GetText(activeDoc.EndPoint);
string editted = Manipulate(text);
//File.WriteAllText("File Address", editted); // I don't want to use this
I want to edit the current opened document such that the user will be able to use ctrl+z to revert any changes the extension makes.

CreateEditPoint returns EnvDTE.EditPoint. You can use, for example, EditPoint.Insert or EditPoint.ReplaceText methods to change the document.

Related

Run script when finished saving file - Visual Studio Extensibility

Can someone give me some sample code for Visual Studio Extensibility where I can grab the text from a document, when the Save event ends, and run a script in C # with that text (example, trigger a Web service for certain file extensions). It could also be a new button (for example, save in the web service).
You can subscribe to the DocumentSaved event:
events = DTE.Events;
documentEvents = events.DocumentEvents;
documentEvents.DocumentSaved += OnDocumentSaved;
In the OnDocumentSaved handler with EnvDTE.Document you can get the document path as doc.FullName.
To get text from EnvDTE.Document:
TextDocument td = (TextDocument)(doc.Object("TextDocument"));
var p = td.StartPoint.CreateEditPoint();
string s = p.GetText(td.EndPoint);
See In VisualStudio DTE, how to get the contents of the ActiveDocument? and https://vlasovstudio.com/visual-commander/extensions.html for complete samples.

Extension for Specflow in visual studio

I have a set of tests using specflow on Visual Studio, some of them have steps that looks like:
Given the data in file /foo/bar/data.txt
I would like to implement a Visual Studio extension so I can click on /foo/bar/data.txt and get the file opened.
I had a vague idea of using something like a Visual Studio text adorn, but I really don't know if there is a simpler way. Moreover, I'm looking for a solution that works in Visual Studio 2013 and above, and adorns are not supported in older versions as far as I know. Any ideas?
If you prefix the file path with file:// it will become a clickable link. Replace spaces with "%20" as you would with any URL.
I know that's not the answer to the question you are asking, but maybe you don't need to implement an extension?
One possible solution is to create a new menu entry with a Visual Studio Add-in, this way when you click in a line, and choose this menu option, you can execute an action (read and parse the line and open the file). This can be done as follows:
File->New Project->Other Types -> Extensibility -> Visual Studio Add-in, and the implement IDTCommandTarget
Commands2 commands = (Commands2)_applicationObject.Commands;
object[] contextGUIDS = new object[] { };
CommandBars cmdBars = (CommandBars)(_applicationObject.CommandBars);
CommandBar vsBarProject = cmdBars["Code Window"];
scenarioCommand = commands.AddNamedCommand2(_addInInstance, "OpenScenario", "Open scenario", "Open scenario data", true);
scenarioCommand.AddControl(vsBarProject);
Then in the Exec method, just read the line, get the file path, and:
Process.Start(resource)
And of course, just show the menu option if is a specFlow file in the QueryStatus method:
dynamic docName = _applicationObject.ActiveDocument.FullName;
if (CmdName == OpenScenarioCmd && !((string)docName).EndsWith(".feature"))
{
StatusOption = (vsCommandStatus)vsCommandStatus.vsCommandStatusInvisible;
}
else if (CmdName == OpenScenarioCmd)
{
StatusOption = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
}
It's not perfect, because you have to show a menu, but it works.

Resharper compile time autoformat incorrectly changes code

Resharper changes below code
string strTest = "Test";
string strTest2 = "Test2";
to this
string strTest = "Test";string strTest2 = "Test2";
if cursor is at the end of the first line when I start project. It makes all breakpoints obsolete ("The breakpoint will not currently be hit. The source code is different from the original version.")
And sometimes it mixes comment line with code line and completely breaks execution. For instance:
//Comment line
string strTest = "Test";
changes to
//Comment linestring strTest = "Test";
If cursor is between double quotes it doesn't modify code.
If I suspend ReSharper plugin code doesn't change on compile time so I am pretty sure that ReSharper has some problems. I tried disabling formatting on ReSharper options but it still modifies code.
How can I disable this feature? Other formatting options seems ok (Both VS and ReSharper) so if I just disable compile time auto corrections it will be ok. I couldn't find any option for this.
PS: I use VS2013 with VSCommands for VS2013 extension. ReSharper version is 10.0.2.
Solution: As #Alexander mentioned it is related to DevExpress components. Emptying the licences.licx file contents and restarting visual studio/Clean&Rebuild project resolves the issue. This prebuild-event script solves the issue.
break>$(ProjectDir)\Properties\licenses.licx

IVsInvisibleEditorManager not placing document within Running Document Table

I'm working on a Visual Studio package and I seem to be running into an issue with IVsInvisibleEditorManager and the Running Document Table (RDT).
To start, I have a file opened within a normal Visual Studio editor. Next, I register an IVsInvisibleEditor for this same file via:
IVsInvisibleEditor invisibleEditor;
ErrorHandler.ThrowOnFailure(this._InvisibleEditorManager.RegisterInvisibleEditor(
filePath
, pProject: null
, dwFlags: (uint)_EDITORREGFLAGS.RIEF_ENABLECACHING
, pFactory: null
, ppEditor: out invisibleEditor));
When I modify the file and close the primary Visual Studio editor, I am prompted with a message to save my document. My understanding is that this should not be the case as I still have access to this file within my invisible editor. Visual Studio then cleans up some of the resources associated with this file, which breaks my invisible editor.
I suspect this is because RegisterInvisibleEditor() is not correctly registering my document within the RDT.
The documentation for RegisterInvisibleEditor() states the following for dwFlags when using _EDITORREGFLAGS.RIEF_ENABLECACHING:
This allows the document to stay in the RDT in the scenario where a
document is open in a visible editor, and closed by the user while an
invisible editor is registered for that document.
This describes my problem exactly. The visible editor is being closed, but I'd like the document to remain in the RDT.
Does anyone know how to make my document persists within the RDT?
Is the RDT project specific? Does the fact that I'm passing in null for both pProject and pFactory cause any problems for the RDT?
Edit: I just tested the above code out, but passed in the appropriate IVsProject and there was no change. It still appears the RDT is not changed when registering an invisible editor.
It doesn't appear as though I can convince the IVsInvisibleEditorManager to add a lock to the document. Unfortunately, RegisterInvisibleEditor() is a COM method, which means I can't decompile and peek at what it's doing (at least to my limited knowledge).
However, I've come up with a workaround in which I manually manage entries within the RDT.
var rdt =
(IVsRunningDocumentTable)GetService(typeof(SVsRunningDocumentTable));
//Retrieve the appropriate IVsHierarchy. I've assumed there's only
//one project within this solution.
var solutionService = (IVsSolution)GetService(typeof(SVsSolution));
IVsProject project = null;
Guid guid = Guid.Empty;
solutionService.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref guid, out enumerator);
var hierarchy = new IVsHierarchy[1] { null };
uint fetched = 0;
for((enumerator.Reset(); enumerator.Next(1, hierarchy, out fetched) == VSConstants.S_OK && fetched == 1;)
hierarchy = hierarchyArray[0]
//Then when creating the IVsInvisibleEditor, find and lock the document
uint itemID;
IntPtr docData;
uint docCookie;
var result = rdt.FindAndLockDocument(
dwRDTLockType: (uint)_VSRDTFLAGS.RDT_ReadLock
, pszMkDocument: filePath
, ppHier: out hierarchy
, pitemid: out itemID
, ppunkDocData: out docData
, pdwCookie: out docCookie
);
At some point you'll be finished with your IVsInvisibleEditor, at which point you should unlock the document from the RDT.
runningDocTable.UnlockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, docCookie);

Send Custom Action Data via Command Line for Visual Studio Installer

I have a Visual Studio Installer that has a custom UI with one text box recovering a value that is set to QUEUEDIRECTORY property. Then I have a custom action (an Installer class) that passes in that property value with this line /queuedir="[QUEUEDIRECTORY]" - and the installer works great.
Now, I need to send that value via the command-line so that this installer can be run by system administrators all across the organization. So, I tried the following command line statements but it just doesn't work.
msiexec /i Setup.msi QUEUEDIRECTORY="D:\temp"
Setup.msi QUEUEDIRECTORY="D:\temp"
Setup.msi queuedir="D:\temp"
msiexec /i Setup.msi queuedir="D:\temp"
Further, I can't seem to find anything online that doesn't feel like they hacked it because they just couldn't find the solution. I mean I've found some solutions where they are editing the MSI database and everything, but man that just doesn't seem like it's the right solution - especially since I'm using Visual Studio 2010 - Microsoft has surely made some enhancements since its initial release of this offering.
Here is one of the articles that appears would work but still really feels like a hack.
At any rate, I hope that you can help me out!
This is what I did to add command line only property values to my MSI in Visual Studio 2010. It's similar to the accepted answer, but less hacky. Create CommandLineSupport.js in setup project (.vdproj) directory, with the following code:
//This script adds command-line support for MSI installer
var msiOpenDatabaseModeTransact = 1;
if (WScript.Arguments.Length != 1)
{
WScript.StdErr.WriteLine(WScript.ScriptName + " file");
WScript.Quit(1);
}
WScript.Echo(WScript.Arguments(0));
var filespec = WScript.Arguments(0);
var installer = WScript.CreateObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql
var view
try
{
sql = "INSERT INTO `Property` (`Property`, `Value`) VALUES ('MYPROPERTY', 'MYPROPERTY=\"\"')";
view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();
}
catch(e)
{
WScript.StdErr.WriteLine(e);
WScript.Quit(1);
}
Then click on your Deployment Project in Visual Studio to view the Properties of the project, and set the PostBuildEvent to this:
cscript.exe "$(ProjectDir)CommandLineSupport.js" "$(BuiltOuputPath)"
Then set up the Delopyment Project with a Custom Action. Click on the Primary Output to get to the Custom Action Properties, and set the CustomActionData field to /MYPROPERTY="[MYPROPERTY]"
You can then access that property in your Custom Action installer class like this:
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
string the_commandline_property_value = Context.Parameters["MYPROPERTY"].ToString();
}
In the end you can run the cmd. C:\>Setup.msi MYPROPERTY=VALUE
This doesn't require any messing about in Orca, or using any custom dialog controls like in the accepted answer. You don't have to modify the PostBuildEvent to have the correct .msi name either. Etc. Also can add as many properties as you wish like this:
INSERT INTO `Property` (`Property`, `Value`) VALUES ('MYPROPERTY', 'MYPROPERTY=\"\"'),('MYPROPERTY2', 'MYPROPERTY2=\"\"', ('MYPROPERTY3', 'MYPROPERTY3=\"\"')) ";
Have fun!
Alright, so I ended up going with the solution I linked to in the question. But let me put the script here for completeness. The first thing I needed to do was build a JS file that had the following code (I named it CommandLineSupport.js) and put it in the same directory as the .vdproj.
//This script adds command-line support for MSI build with Visual Studio 2008.
var msiOpenDatabaseModeTransact = 1;
if (WScript.Arguments.Length != 1)
{
WScript.StdErr.WriteLine(WScript.ScriptName + " file");
WScript.Quit(1);
}
WScript.Echo(WScript.Arguments(0));
var filespec = WScript.Arguments(0);
var installer = WScript.CreateObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql
var view
try
{
//Update InstallUISequence to support command-line parameters in interactive mode.
sql = "UPDATE InstallUISequence SET Condition = 'QUEUEDIRECTORY=\"\"' WHERE Action = 'CustomTextA_SetProperty_EDIT1'";
view = database.OpenView(sql);
view.Execute();
view.Close();
//Update InstallExecuteSequence to support command line in passive or quiet mode.
sql = "UPDATE InstallExecuteSequence SET Condition = 'QUEUEDIRECTORY=\"\"' WHERE Action = 'CustomTextA_SetProperty_EDIT1'";
view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();
}
catch(e)
{
WScript.StdErr.WriteLine(e);
WScript.Quit(1);
}
You of course would need to ensure that you're replacing the right Action by opening the MSI in Orca and matching that up to the Property on the custom dialog you created.
Next, now that I had the JS file working, I needed to add a PostBuildEvent to the .vdproj and you can do that by clicking on the setup project in Visual Studio and hitting F4. Then find the PostBuildEvent property and click the elipses. In that PostBuildEvent place this code:
cscript "$(ProjectDir)CommandLineSupport.js" "$(BuildOutputPath)Setup.msi"
Making sure to replace Setup.msi with the name of your MSI file.
Though I still feel like it's a hack ... because it is ... it works and will do the job for now. It's a small enough project that it's really not a big deal.
This is an old thread, but there's a simpler, working solution that still seems hard to find, hence why I'm posting it here.
In my scenario we're working with VS2013 (Community Ed.) and VS 2013 Installer Project extension. Our installer project has a custom UI step collecting two user texts and a custom action bound to Install\Start step that receives those texts.
We were able to make this work from GUI setup wizard, but not from command line. In the end, following this workaround, we were able to make also command line work, without any MSI file Orca editing.
The gist of the thing was to set a value for all needed custom dialog properties directly from Visual Studio, and such value should be in the form [YOUR_DIALOG_PROPERTY_NAME]. Also, it seems like such "public" properties must be named in all caps.
Here's the final setup:
Custom dialog properties
Note e.g. Edit1Property and Edit1Value.
Custom action properties
Note as property key used later in code can be named in camel case.
Custom action code
string companyId = Context.Parameters["companyId"];
string companyApiKey = Context.Parameters["companyApiKey"];
Command line
> setup.exe COMPANYID="Some ID" COMPANYAPIKEY="Some KEY" /q /l mylog.txt
HTH

Categories