views:

268

answers:

2

I am working on extending the spell checker that has recently been updated on the code gallery: http://code.msdn.microsoft.com/spellCheckerSample

I would like to update it so that the "ignore" list of words comes from CustomDictionary.xml, the same file that the static code analysis tools use.

From within my VS 2010 editor extension, I need to:

  1. Find the project in which the current file resides.
  2. Find the "CustomDictionary.XML" file, if it exists.
  3. If it exists, I need to check it out from source control
  4. If it doesn't exist, I need to add it to the current project (and to the source control)
  5. Add text to the CustomDictionary.XML file (this is the easy part).

Is anyone aware of any examples that would help me learn how to do this? I am using the VS2010 Release Candidate.

Thanks.

~ Cameron

Here's what I have so far:

public ISpellingDictionaryService GetSpellingDictionaryService(ITextBuffer textBuffer)
{
    var bufferAdapter = AdaptersFactory.GetBufferAdapter(textBuffer);
    if (bufferAdapter != null)
    {
        var extensibleObject = bufferAdapter as IExtensibleObject;
        if (extensibleObject != null)
        {
            object document;
            extensibleObject.GetAutomationObject("Document", null, out document);
            // Control never gets here... GetAutomationObject appears to recurse.
        }
    }
}
+2  A: 

First, it would probably be best to base your extension of the updated version of the spell checker sample that I released; you can grab the source on github.

Find the project of a given buffer

Assuming you start with an ITextBuffer, you can get to the DTE Project by doing:

  1. In whatever MEF component you have (for the spell checker, the SpellingDictionaryService is the best place), add the following MEF import:

    [Import] IVsEditorAdaptersFactoryService AdaptersFactory = null;

    Also, add a reference to Microsoft.VisualStudio.Editor.dll to the project, if it isn't there already.

  2. Either modify the methods on the ISpellingDictionaryService to also take an ITextBuffer, or modify the service to return a spelling dictionary instance that is specific to a buffer/project (and then the methods don't need to have one passed explicitly to each call).
  3. Use AdaptersFactory.GetBufferAdapter to get the VS buffer adapter (IVsTextBuffer).
  4. Cast this to IExtensibleObject, and call IExtensibleObject.GetAutomationObject("Document") to get the DTE Document for the buffer.
  5. From here, you can use the ProjectItem property, then ContainingProject

Find CustomDictionary.xml file or add it, if necessary

Once you have the Project, you can use project.ProjectItems.Item("CustomDictionary.xml") to find it (though I think this may only work for the top-level directory), which throws an ArgumentException if it doesn't exist. If you need to add it, then, you can write the file to disk in the project location and use project.ProjectItems.AddFromFile(string filename) to add it. You could also check for the existence of the project file by just looking for it on disk first (creating or just writing to it), and then calling AddFromFile at the end, either only calling it if you created the file or calling it anyways and handling the exception. When you have added it and finished modifying it, you can get the ProjectItem for that file and use the ProjectItem.Save method to save it to disk.

Check out or add to source control

From the Project DTE object (or any DTE object, actually), you can use the SourceControl property, which will let you check if the item is under source control and check it out. However, I think this only has the ability to let you "check out" a file (I think this does a tf edit, if you are using TFS), and not the ability to add a file to source control. I'm not sure the best way to do this programmatically, sorry.

Some references, from MSDN:

Top-level DTE object members
DTE.Project members
DTE.SourceControl members

Noah Richards
Cool... I have the latest source now.I could be missing something, or perhaps I'm not using all the right namespaces...The MSDN help on IExtensibleObject suggests that it is for internal use only: http://msdn.microsoft.com/en-us/library/envdte.iextensibleobject_members(VS.100).aspx. It also requires an interface to IExtensibleObjectSite, which I don't know how to provide.Am I in the wrong namespace? I have added references to: EnvDTE.dll and Microsoft.VisualStudio.Shell.Interop.
Cameron Peters
So far as I know, it isn't really internal use only. Also, I think you can always pass null for the site (internally, I think the buffer tends to ignore the site for most things anyways).
Noah Richards
GetAutomationObject("Document", null, out document) appears to cause recursion. I am getting a stack overflow when I run it.
Cameron Peters
Can you paste the stack someplace so I can take a look? That certainly isn't expected :)
Noah Richards
+1  A: 

Got it working with a combination of Noah's help and something I found on the MSDN forums. I'm not sure it would be better to get the DTE object another way, but this is working as far as I can see:

var bufferAdapter = AdaptersFactory.GetBufferAdapter(textBuffer);
if (bufferAdapter != null)
{
    var persistFileFormat = bufferAdapter as IPersistFileFormat;
    if (persistFileFormat != null)
    {
        string ppzsFilename;
        uint iii;
        persistFileFormat.GetCurFile(out ppzsFilename, out iii);

        var dte2 = (DTE2) Shell.Package.GetGlobalService(typeof (SDTE));

        ProjectItem prjItem = dte2.Solution.FindProjectItem(ppzsFilename);

        var containingProject = prjItem.ContainingProject;

        // Now use the containingProject.Items to find my file, etc, etc
    }
}
Cameron Peters
The problem with this solution is that ppzsFilename can be null in some instances, which means that I'm then not able to find the containing project.... sigh.
Cameron Peters