ExpansionProvider.cs :  » Development » StyleCop » Microsoft » VisualStudio » Package » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Development » StyleCop 
StyleCop » Microsoft » VisualStudio » Package » ExpansionProvider.cs
//#define TRACE_EXEC
using System;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using System.Runtime.InteropServices;
using System.Collections;
using System.IO;
using Microsoft.Win32;
using IOleServiceProviderMicrosoft.VisualStudio.OLE.Interop.IServiceProvider;
using IServiceProviderSystem.IServiceProvider;
using System.Diagnostics;
using System.Xml;
using System.Text;
using VsCommandsMicrosoft.VisualStudio.VSConstants.VSStd97CmdID;
using VsCommands2KMicrosoft.VisualStudio.VSConstants.VSStd2KCmdID;

namespace Microsoft.VisualStudio.Package{

    /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="DefaultFieldValue"]/*' />
    public class DefaultFieldValue {
        private string field;
        private string value;

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="DefaultFieldValue.DefaultFieldValue"]/*' />
        public DefaultFieldValue(string field, string value) {
            this.field = field;
            this.value = value;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="DefaultFieldValue.Field"]/*' />
        public string Field {
            get { return this.field; }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="DefaultFieldValue.Value"]/*' />
        public string Value {
            get { return this.value; }
        }
    }

    /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider"]/*' />
    [CLSCompliant(false)]
    [System.Runtime.InteropServices.ComVisible(true)]
    public class ExpansionProvider : IDisposable, IVsExpansionClient {
        IVsTextView view;
        Source source;
        IVsExpansion vsExpansion;
        IVsExpansionSession expansionSession;
        bool expansionActive;
        bool expansionPrepared;
        bool completorActiveDuringPreExec;
        ArrayList fieldDefaults; // CDefaultFieldValues
        string titleToInsert;
        string pathToInsert;

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.ExpansionProvider"]/*' />
        public ExpansionProvider(Source src) {
            if (src == null){
                throw new ArgumentNullException("src");
            }
            this.fieldDefaults = new ArrayList();
            if (src == null)
                throw new System.ArgumentNullException();

            this.source = src;
            this.vsExpansion = null; // do we need a Close() method here?

            // QI for IVsExpansion
            IVsTextLines buffer = src.GetTextLines();
            this.vsExpansion = (IVsExpansion)buffer;
            if (this.vsExpansion == null) {
                throw new ArgumentNullException("(IVsExpansion)src.GetTextLines()");
            }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.Finalize"]/*' />
        ~ExpansionProvider() {
#if LANGTRACE
            Trace.WriteLine("~ExpansionProvider");
#endif
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.Dispose"]/*' />
        public virtual void Dispose() {
            EndTemplateEditing(true);
            this.source = null;
            this.vsExpansion = null;
            this.view = null;
            GC.SuppressFinalize(this);
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.Source"]/*' />
        public Source Source {
            get { return this.source; }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.TextView"]/*' />
        public IVsTextView TextView {
            get { return this.view; }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.Expansion"]/*' />
        public IVsExpansion Expansion {
            get { return this.vsExpansion; }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.ExpansionSession"]/*' />
        public IVsExpansionSession ExpansionSession {
            get { return this.expansionSession; }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.HandleQueryStatus"]/*' />
        public virtual bool HandleQueryStatus(ref Guid guidCmdGroup, uint nCmdId, out int hr) {
            // in case there's something to conditinally support later on...
            hr = 0;
            return false;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.InTemplateEditingMode"]/*' />
        public virtual bool InTemplateEditingMode {
            get {
                return this.expansionActive;
            }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.InTemplateEditingMode"]/*' />
        public virtual TextSpan GetExpansionSpan() {
            if (this.expansionSession == null){
                throw new System.InvalidOperationException(SR.GetString(SR.NoExpansionSession));
            }
            TextSpan[] pts = new TextSpan[1];
            int hr = this.expansionSession.GetSnippetSpan(pts);
            if (NativeMethods.Succeeded(hr)) {
                return pts[0];
            }
            return new TextSpan();
        }


        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.HandlePreExec"]/*' />
        public virtual bool HandlePreExec(ref Guid guidCmdGroup, uint nCmdId, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) {
            if (!this.expansionActive || this.expansionSession == null) {
        return false;
            }

            this.completorActiveDuringPreExec = this.IsCompletorActive(this.view);            

            if (guidCmdGroup == typeof(VsCommands2K).GUID) {
                VsCommands2K cmd = (VsCommands2K)nCmdId;
#if TRACE_EXEC
                Trace.WriteLine(String.Format("ExecCommand: {0}", cmd.ToString()));
#endif
                switch (cmd) {
                    case VsCommands2K.CANCEL:
                        if (this.completorActiveDuringPreExec)
                            return false;
                        EndTemplateEditing(true);
                        return true;
                    case VsCommands2K.RETURN:
                        bool leaveCaret = false;
                        int line = 0, col = 0;
                        if (NativeMethods.Succeeded(this.view.GetCaretPos(out line, out col))) {
                            TextSpan span = GetExpansionSpan();
                            if (!TextSpanHelper.ContainsExclusive(span, line, col)) {
                                leaveCaret = true;
                            }
                        }
                        if (this.completorActiveDuringPreExec)
                            return false;
                        EndTemplateEditing(leaveCaret);
                        if (leaveCaret)
                            return false;
                        return true;
                    case VsCommands2K.BACKTAB:
                        if (this.completorActiveDuringPreExec)
                            return false;
                        this.expansionSession.GoToPreviousExpansionField();
                        return true;
                    case VsCommands2K.TAB:
                        if (this.completorActiveDuringPreExec)
                            return false;
                        this.expansionSession.GoToNextExpansionField(0); // fCommitIfLast=false
                        return true;
#if TRACE_EXEC
                    case VsCommands2K.TYPECHAR:
                        if (pvaIn != IntPtr.Zero) {
                            Variant v = Variant.ToVariant(pvaIn);
                            char ch = v.ToChar();
                            Trace.WriteLine(String.Format("TYPECHAR: {0}, '{1}', {2}", cmd.ToString(), ch.ToString(), (int)ch));
                        }
                        return true;
#endif
                }
            }
            return false;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.HandlePostExec"]/*' />
        public virtual bool HandlePostExec(ref Guid guidCmdGroup, uint nCmdId, uint nCmdexecopt, bool commit, IntPtr pvaIn, IntPtr pvaOut) {
            if (guidCmdGroup == typeof(VsCommands2K).GUID) {
                VsCommands2K cmd = (VsCommands2K)nCmdId;
                switch (cmd) {
                    case VsCommands2K.RETURN:
                        if (this.completorActiveDuringPreExec && commit) {
                            // if the completor was active during the pre-exec we want to let it handle the command first
                            // so we didn't deal with this in pre-exec. If we now get the command, we want to end
                            // the editing of the expansion. We also return that we handled the command so auto-indenting doesn't happen
                            EndTemplateEditing(false);
                            this.completorActiveDuringPreExec = false;
                            return true;
                        }
                        break;
                }
            }
            this.completorActiveDuringPreExec = false;
            return false;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.DisplayExpansionBrowser"]/*' />
        public virtual bool DisplayExpansionBrowser(IVsTextView view, string prompt, string[] types, bool includeNullType, string[] kinds, bool includeNullKind) {
            if (this.expansionActive) this.EndTemplateEditing(true);

            if (this.source.IsCompletorActive) {
                this.source.DismissCompletor();
            }

            this.view = view;
            IServiceProvider site = this.source.LanguageService.Site;
            IVsTextManager2 textmgr = site.GetService(typeof(SVsTextManager)) as IVsTextManager2;
            if (textmgr == null) return false;
            
            IVsExpansionManager exmgr;
            textmgr.GetExpansionManager(out exmgr);
            Guid languageSID = this.source.LanguageService.GetLanguageServiceGuid();
            int hr = 0;
            if (exmgr != null) {
                hr = exmgr.InvokeInsertionUI(view, // pView
                    this, // pClient
                    languageSID, // guidLang
                    types, // bstrTypes
                    (types == null) ? 0 : types.Length, // iCountTypes
                    includeNullType ? 1 : 0,  // fIncludeNULLType
                    kinds, // bstrKinds
                    (kinds == null) ? 0 : kinds.Length, // iCountKinds
                    includeNullKind ? 1 : 0, // fIncludeNULLKind
                    prompt, // bstrPrefixText
                    ">" //bstrCompletionChar
                    );
                if (NativeMethods.Succeeded(hr)) {
                    return true;
                }
            }

            return false;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.InsertSpecificExpansion"]/*' />
        public virtual bool InsertSpecificExpansion(IVsTextView view, XmlElement snippet, TextSpan pos, string relativePath) {
            if (this.expansionActive) this.EndTemplateEditing(true);

            if (this.source.IsCompletorActive) {
                this.source.DismissCompletor();
            }

            this.view = view;
            MSXML.IXMLDOMDocument doc = new MSXML.DOMDocumentClass();
            if (!doc.loadXML(snippet.OuterXml)) {
                throw new ArgumentException(doc.parseError.reason);
            }
            Guid guidLanguage = this.source.LanguageService.GetLanguageServiceGuid();

            int hr = this.vsExpansion.InsertSpecificExpansion(doc, pos, this, guidLanguage, relativePath, out this.expansionSession);
            if (hr != NativeMethods.S_OK) {
                this.EndTemplateEditing(true);
            } else {
                // When inserting a snippet it is possible that the edit session is ended inside the insert
                // function (e.g. if the template has no editable fields). In this case we should not stay
                // in template edit mode because otherwise our filter will stole messages to the editor.
                if (!this.expansionActive) {
                    this.expansionSession = null;
                }
                return true;
            }
            return false;
        }

        bool IsCompletorActive(IVsTextView view){
            if (this.source.IsCompletorActive)
                return true;

            IVsTextViewEx viewex = view as IVsTextViewEx;
            if (viewex  != null) {
                return viewex.IsCompletorWindowActive() == VSConstants.S_OK;
            }

            return false;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.InsertNamedExpansion"]/*' />
        public virtual bool InsertNamedExpansion(IVsTextView view, string title, string path, TextSpan pos, bool showDisambiguationUI) {

            if (this.source.IsCompletorActive) {
                this.source.DismissCompletor();
            }

            this.view = view;
            if (this.expansionActive) this.EndTemplateEditing(true);

            Guid guidLanguage = this.source.LanguageService.GetLanguageServiceGuid();

            int hr = this.vsExpansion.InsertNamedExpansion(title, path, pos, this, guidLanguage, showDisambiguationUI ? 1 : 0, out this.expansionSession);

            if (hr != NativeMethods.S_OK) {
                this.EndTemplateEditing(true);
                return false;
            } else if (hr == NativeMethods.S_OK) {
                // When inserting a snippet it is possible that the edit session is ended inside the insert
                // function (e.g. if the template has no editable fields). In this case we should not stay
                // in template edit mode because otherwise our filter will stole messages to the editor.
                if (!this.expansionActive) {
                    this.expansionSession = null;
                }
                return true;
            }
            return false;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.FindExpansionByShortcut"]/*' />
        /// <summary>Returns S_OK if match found, S_FALSE if expansion UI is shown, and error otherwise</summary>
        public virtual int FindExpansionByShortcut(IVsTextView view, string shortcut, TextSpan span, bool showDisambiguationUI, out string title, out string path) {
            if (this.expansionActive) this.EndTemplateEditing(true);
            this.view = view;
            title = path = null;

            LanguageService svc = this.source.LanguageService;
            IVsExpansionManager mgr = svc.Site.GetService(typeof(SVsExpansionManager)) as IVsExpansionManager;
            if (mgr == null) return NativeMethods.E_FAIL ;
            Guid guidLanguage = svc.GetLanguageServiceGuid();

            TextSpan[] pts = new TextSpan[1];
            pts[0] = span;
            int hr = mgr.GetExpansionByShortcut(this, guidLanguage, shortcut, this.TextView, pts, showDisambiguationUI ? 1 : 0, out path, out title);
            return hr;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.GetExpansionFunction1"]/*' />
        public virtual IVsExpansionFunction GetExpansionFunction(XmlElement xmlFunctionNode, string fieldName) {
            string functionName = null;
            ArrayList rgFuncParams = new ArrayList();

            // first off, get the function string from the node
            string function = xmlFunctionNode.InnerText;

            if (function == null || function.Length == 0)
                return null;

            bool inIdent = false;
            bool inParams = false;
            int token = 0;

            // initialize the vars needed for our super-complex function parser :-)
            for (int i = 0, n = function.Length; i < n; i++) {
                char ch = function[i];

                // ignore and skip whitespace
                if (!Char.IsWhiteSpace(ch)) {
                    switch (ch) {
                        case ',':
                            if (!inIdent || !inParams)
                                i = n; // terminate loop
                            else {
                                // we've hit a comma, so end this param and move on...
                                string name = function.Substring(token, i - token);
                                rgFuncParams.Add(name);
                                inIdent = false;
                            }
                            break;
                        case '(':
                            if (!inIdent || inParams)
                                i = n; // terminate loop
                            else {
                                // we've hit the (, so we know the token before this is the name of the function
                                functionName = function.Substring(token, i - token);
                                inIdent = false;
                                inParams = true;
                            }
                            break;
                        case ')':
                            if (!inParams)
                                i = n; // terminate loop
                            else {
                                if (inIdent) {
                                    // save last param and stop
                                    string name = function.Substring(token, i - token);
                                    rgFuncParams.Add(name);
                                    inIdent = false;
                                }
                                i = n; // terminate loop
                            }
                            break;
                        default:
                            if (!inIdent) {
                                inIdent = true;
                                token = i;
                            }
                            break;
                    }
                }
            }

            if (functionName != null && functionName.Length > 0) {
                ExpansionFunction func = this.source.LanguageService.CreateExpansionFunction(this, functionName);
                if (func != null) {
                    func.FieldName = fieldName;
                    func.Arguments = (string[])rgFuncParams.ToArray(typeof(string));
                    return func;
                }
            }
            return null;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.PrepareTemplate"]/*' />
        public virtual void PrepareTemplate(string title, string path) {            
            if (title == null)
                throw new System.ArgumentNullException("title");

            // stash the title and path for when we actually insert the template
            this.titleToInsert = title;
            this.pathToInsert = path;
            this.expansionPrepared = true;
        }

        void SetFieldDefault(string field, string value) {
            if (!this.expansionPrepared) {
                throw new System.InvalidOperationException(SR.GetString(SR.TemplateNotPrepared));
            }
            if (field == null) throw new System.ArgumentNullException("field");
            if (value == null) throw new System.ArgumentNullException("value");

            // we have an expansion "prepared" to insert, so we can now save this
            // field default to set when the expansion is actually inserted
            this.fieldDefaults.Add(new DefaultFieldValue(field, value));
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.BeginTemplateEditing"]/*' />
        public virtual void BeginTemplateEditing(int line, int col) {
            if (!this.expansionPrepared) {
                throw new System.InvalidOperationException(SR.GetString(SR.TemplateNotPrepared));
            }

            TextSpan tsInsert = new TextSpan();
            tsInsert.iStartLine = tsInsert.iEndLine = line;
            tsInsert.iStartIndex = tsInsert.iEndIndex = col;

            Guid languageSID = this.source.LanguageService.GetType().GUID;

            int hr = this.vsExpansion.InsertNamedExpansion(
                this.titleToInsert,
                this.pathToInsert,
                tsInsert,
                (IVsExpansionClient)this,
                languageSID,
                0, // fShowDisambiguationUI,
                out this.expansionSession
            );

            if (hr != NativeMethods.S_OK) {
                this.EndTemplateEditing(true);
            }
            this.pathToInsert = null;
            this.titleToInsert = null;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.EndTemplateEditing"]/*' />
        public virtual void EndTemplateEditing(bool leaveCaret) {
            if (!this.expansionActive || this.expansionSession == null) {
                this.expansionActive = false;
                return;
            }

            this.expansionSession.EndCurrentExpansion(leaveCaret ? 1 : 0); // fLeaveCaret=true
            this.expansionSession = null;
            this.expansionActive = false;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.GetFieldSpan"]/*' />
        public virtual bool GetFieldSpan(string field, out TextSpan pts) {
            if (this.expansionSession == null) {
                throw new System.InvalidOperationException(SR.GetString(SR.NoExpansionSession));
            }
            if (this.expansionSession != null) {
                TextSpan[] apt = new TextSpan[1];
                this.expansionSession.GetFieldSpan(field, apt);
                pts = apt[0];
                return true;
            } else {
                pts = new TextSpan();
                return false;
            }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.GetFieldValue"]/*' />
        public virtual bool GetFieldValue(string field, out string value) {
            if (this.expansionSession == null) {
                throw new System.InvalidOperationException(SR.GetString(SR.NoExpansionSession));
            }
            if (this.expansionSession != null) {
                this.expansionSession.GetFieldValue(field, out value);
            } else {
                value = null;
            }
            return value != null;
        }

        #region IVsExpansionClient Members

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.EndExpansion"]/*' />
        public int EndExpansion() {
            this.expansionActive = false;
            this.expansionSession = null;
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.FormatSpan"]/*' />
        public virtual int FormatSpan(IVsTextLines buffer, TextSpan[] ts) {
            if (this.source.GetTextLines() != buffer) {
                throw new System.ArgumentException(SR.GetString(SR.UnknownBuffer), "buffer");
            }
            int rc = NativeMethods.E_NOTIMPL;
            if (ts != null) {
                for (int i = 0, n = ts.Length; i < n; i++) {
                    if (this.source.LanguageService.Preferences.EnableFormatSelection) {
                        TextSpan span = ts[i];
                        // We should not merge edits in this case because it might clobber the
                        // $varname$ spans which are markers for yellow boxes.
                        using (EditArray edits = new EditArray(this.source, this.view, false, SR.GetString(SR.FormatSpan))) {
                            this.source.ReformatSpan(edits, span);
                            edits.ApplyEdits();
                        }
                        rc = NativeMethods.S_OK;
                    }
                }
            }
            return rc;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.IsValidKind"]/*' />
        public virtual int IsValidKind(IVsTextLines buffer, TextSpan[] ts, string bstrKind, out int /*BOOL*/ fIsValid)
        {
            fIsValid = 0;
            if (this.source.GetTextLines() != buffer)
            {
                throw new System.ArgumentException(SR.GetString(SR.UnknownBuffer), "buffer");
            }

            fIsValid = 1;
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.IsValidType"]/*' />
        public virtual int IsValidType(IVsTextLines buffer, TextSpan[] ts, string[] rgTypes, int iCountTypes, out int /*BOOL*/ fIsValid)
        {
            fIsValid = 0;
            if (this.source.GetTextLines() != buffer) {
                throw new System.ArgumentException(SR.GetString(SR.UnknownBuffer), "buffer");
            }
            
            fIsValid = 1;
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.OnItemChosen"]/*' />
        public virtual int OnItemChosen(string pszTitle, string pszPath) {
            TextSpan ts;
            view.GetCaretPos(out ts.iStartLine, out ts.iStartIndex);
            ts.iEndLine = ts.iStartLine;
            ts.iEndIndex = ts.iStartIndex;

            if (this.expansionSession != null) { // previous session should have been ended by now!
                EndTemplateEditing(true);
            }

            Guid languageSID = this.source.LanguageService.GetType().GUID;

            // insert the expansion
            using (CompoundActionBase cab = CompoundActionFactory.GetCompoundAction(this.view, this.source, SR.FormatSpan)) { 
                return this.vsExpansion.InsertNamedExpansion(pszTitle,
                    pszPath, 
                    ts,
                    (IVsExpansionClient)this,
                    languageSID,
                    0, // fShowDisambiguationUI, (FALSE)
                    out this.expansionSession);
            }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.PositionCaretForEditing"]/*' />
        public virtual int PositionCaretForEditing(IVsTextLines pBuffer, TextSpan[] ts) {
            // NOP
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.OnAfterInsertion"]/*' />
        public virtual int OnAfterInsertion(IVsExpansionSession session) {
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.OnBeforeInsertion"]/*' />
        public virtual int OnBeforeInsertion(IVsExpansionSession session) {
            if (session == null)
                return NativeMethods.E_UNEXPECTED;

            this.expansionPrepared = false;
            this.expansionActive = true;

            // stash the expansion session pointer while the expansion is active
            if (this.expansionSession == null) {
                this.expansionSession = session;
            } else {
                // these better be the same!
                Debug.Assert(this.expansionSession == session);
            }

            // now set any field defaults that we have.
            foreach (DefaultFieldValue dv in this.fieldDefaults) {
                this.expansionSession.SetFieldDefault(dv.Field, dv.Value);
            }
            this.fieldDefaults.Clear();
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.GetExpansionFunction"]/*' />
        public virtual int GetExpansionFunction(MSXML.IXMLDOMNode xmlFunctionNode, string fieldName, out IVsExpansionFunction func) {

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlFunctionNode.xml);
            func = GetExpansionFunction(doc.DocumentElement, fieldName);
            return NativeMethods.S_OK;
        }

        #endregion

    }


    /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction"]/*' />
    [CLSCompliant(false)]
    [System.Runtime.InteropServices.ComVisible(true)]
    public abstract class ExpansionFunction : IVsExpansionFunction {
        ExpansionProvider provider;
        string fieldName;
        string[] args;
        string[] list;

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.ExpansionFunction"]/*' />
        /// <summary>You must construct this object with an ExpansionProvider</summary>
        private ExpansionFunction() {
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.ExpansionFunction2"]/*' />
        public ExpansionFunction(ExpansionProvider provider) {
            this.provider = provider;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.ExpansionProvider"]/*' />
        public ExpansionProvider ExpansionProvider {
            get { return this.provider; }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.Arguments"]/*' />
        public string[] Arguments {
            get { return this.args; }
            set { this.args = value; }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.FieldName"]/*' />
        public string FieldName {
            get { return this.fieldName; }
            set { this.fieldName = value; }
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetCurrentValue"]/*' />
        public abstract string GetCurrentValue();

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetDefaultValue"]/*' />
        public virtual string GetDefaultValue() {
            // This must call GetCurrentValue sincs during initialization of the snippet
            // VS will call GetDefaultValue and not GetCurrentValue.
            return GetCurrentValue();
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetIntellisenseList"]/*' />
        /// <summary>Override this method if you want intellisense drop support on a list of possible values.</summary>
        public virtual string[] GetIntellisenseList() {
            return null;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetArgument"]/*' />
        /// <summary>
        /// Gets the value of the specified argument, resolving any fields referenced in the argument.
        /// In the substitution, "$$" is replaced with "$" and any floating '$' signs are left unchanged,
        /// for example "$US 23.45" is returned as is.  Only if the two dollar signs enclose a string of
        /// letters or digits is this considered a field name (e.g. "$foo123$").  If the field is not found
        /// then the unresolved string "$foo" is returned.
        /// </summary>
        public string GetArgument(int index) {
            if (args == null || args.Length == 0 || index > args.Length) return null;
            string arg = args[index];
            if (arg == null) return null;
            int i = arg.IndexOf('$');
            if (i >= 0) {
                StringBuilder sb = new StringBuilder();
                int len = arg.Length;
                int start = 0;

                while (i >= 0 && i + 1 < len) {
                    sb.Append(arg.Substring(start, i - start));
                    start = i;
                    i++;
                    if (arg[i] == '$') {
                        sb.Append('$');
                        start = i + 1; // $$ is resolved to $.
                    } else {
                        // parse name of variable.
                        int j = i;
                        for (; j < len; j++) {
                            if (!Char.IsLetterOrDigit(arg[j]))
                                break;
                        }
                        if (j == len) {
                            // terminating '$' not found.
                            sb.Append('$');
                            start = i;
                            break;
                        } else if (arg[j] == '$') {
                            string name = arg.Substring(i, j - i);
                            string value;
                            if (GetFieldValue(name, out value)) {
                                sb.Append(value);
                            } else {
                                // just return the unresolved variable.
                                sb.Append('$');
                                sb.Append(name);
                                sb.Append('$');
                            }
                            start = j + 1;
                        } else {
                            // invalid syntax, e.g. "$US 23.45" or some such thing                            
                            sb.Append('$');
                            sb.Append(arg.Substring(i, j - i));
                            start = j;
                        }
                    }
                    i = arg.IndexOf('$', start);
                }
                if (start < len) {
                    sb.Append(arg.Substring(start, len - start));
                }
                arg = sb.ToString();
            }
            // remove quotes around string literals.
            if (arg.Length > 2 && arg[0] == '"' && arg[arg.Length - 1] == '"') {
                arg = arg.Substring(1, arg.Length - 2);
            } else if (arg.Length > 2 && arg[0] == '\'' && arg[arg.Length - 1] == '\'') {
                arg = arg.Substring(1, arg.Length - 2);
            }
            return arg;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetFieldValue"]/*' />
        public bool GetFieldValue(string name, out string value) {
            value = null;
            if (this.provider != null && this.provider.ExpansionSession != null) {
                int hr = this.provider.ExpansionSession.GetFieldValue(name, out value);
                return NativeMethods.Succeeded(hr);
            }
            return false;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="DefaultFieldValue.GetSelection"]/*' />
        public TextSpan GetSelection() {
            TextSpan result = new TextSpan();
            ExpansionProvider provider = this.ExpansionProvider;
            if (provider != null && provider.TextView != null) {
                NativeMethods.ThrowOnFailure(provider.TextView.GetSelection(out result.iStartLine,
                    out result.iStartIndex, out result.iEndLine, out result.iEndIndex));
            }
            return result;
        }

        #region IVsExpansionFunction Members

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.FieldChanged"]/*' />
        public virtual int FieldChanged(string bstrField, out int fRequeryValue) {
            // Returns true if we care about this field changing.
            // We care if the field changes if one of the arguments refers to it.
            if (this.args != null) {
                string var = "$" + bstrField + "$";
                foreach (string arg in this.args) {
                    if (arg == var) {
                        fRequeryValue = 1; // we care!
                        return NativeMethods.S_OK;
                    }
                }
            }
            fRequeryValue = 0;
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetCurrentValue1"]/*' />
        public int GetCurrentValue(out string bstrValue, out int hasDefaultValue) {
            try {
                bstrValue = this.GetCurrentValue();
            } catch {
                bstrValue = String.Empty;
            }
            hasDefaultValue = (bstrValue == null) ? 0 : 1;
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetDefaultValue1"]/*' />
        public int GetDefaultValue(out string bstrValue, out int hasCurrentValue) {
            try {
                bstrValue = this.GetDefaultValue();
            } catch {
                bstrValue = String.Empty;
            }
            hasCurrentValue = (bstrValue == null) ? 0 : 1;
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetFunctionType"]/*' />
        public virtual int GetFunctionType(out uint pFuncType) {
            if (this.list == null) {
                this.list = this.GetIntellisenseList();
            }
            pFuncType = (this.list == null) ? (uint)_ExpansionFunctionType.eft_Value : (uint)_ExpansionFunctionType.eft_List;
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetListCount"]/*' />
        public virtual int GetListCount(out int iListCount) {
            if (this.list == null) {
                this.list = this.GetIntellisenseList();
            }
            if (this.list != null) {
                iListCount = this.list.Length;
            } else {
                iListCount = 0;
            }
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.GetListText"]/*' />
        public virtual int GetListText(int iIndex, out string ppszText) {
            if (this.list == null) {
                this.list = this.GetIntellisenseList();
            }
            if (this.list != null) {
                ppszText = this.list[iIndex];
            } else {
                ppszText = null;
            }
            return NativeMethods.S_OK;
        }

        /// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionFunction.ReleaseFunction"]/*' />
        public virtual int ReleaseFunction() {
            this.provider = null;
            return NativeMethods.S_OK;
        }

        #endregion
    }

    [Guid("4970C2BC-AF33-4a73-A34F-18B0584C40E4")]
    internal class SVsExpansionManager {
    }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.