none
GetMethods from AuthoringScope is never called

    Question

  • Hi there,

    I'm working on MPF package containing the language service. I need to implement MethodTips feature.

    I've created the class inherited from the AuthoringScope class from MPF and override following methods:

    GetDataTipText, GetDeclarations, GetMethods, Goto.

    All of these except GetMethods are called ok but GetMethods is never called.

    Could you please propose the reason for such weird thing?

    Monday, November 20, 2006 7:58 AM

Answers

  • Dmitry,

    Thanks for pointing me in the right direction! I'm using the MPPG (Yacc) parser and this worked perfect.

    Thanks,

    John

     

    Monday, December 18, 2006 6:34 PM

All replies

  • I’m fighting with it already a week – I have no ideas why this called in IronPython but not called in my package.

     

    Could anybody from MPF team post any ideas here? 

    Monday, November 20, 2006 4:58 PM
  • I’ve gone through the language service sources and I found following:

     

    Source.cs:

     

    public virtual void MethodTip(IVsTextView textView, int line, int index, TokenInfo info) {

        this.BeginParse(line, index, info, ParseReason.MethodTip, textView, new ParseResultHandler(this.HandleMethodTipResponse));

    }

     

    This method (MethodTip) is overridden in my Source class and I break into the debugger after I type ‘(’ in my sources file.

    In my overridden MethodTip method I just call base implementation of MethodTip method.

     

    internal void HandleMethodTipResponse(ParseRequest req) {

        try {

            if (this.service == null || req.Timestamp != this.requestSync)

                return;

     

            CallInfo call = req.Sink.MethodCalls.GetCurrentMethodCall();

            if (call == null) goto fail;

            StringCollection names = call.names;

            if (names.Count == 0) goto fail;

            ArrayList contexts = call.sourceLocations;

            string name = names[names.Count - 1];

            TextSpan span = (TextSpan)contexts[names.Count - 1];

            Methods methods = req.Scope.GetMethods(span.iStartLine, span.iStartIndex, name);

     

            if (methods == null)

                goto fail;

     

            int currentParameter = call.currentParameter;

            this.methodData.Refresh(req.View, methods, currentParameter, span);

            return;

        fail:

            DismissCompletor();

    #if LANGTRACE

        } catch (Exception e) {

            Trace.WriteLine("HandleMethodTipResponse exception: " + e.Message);

    #endif

        } catch {

        }

    }

     

    This method (HandleMethodTipResponse) calls GetMethods of AuthoringScope which I overridden in my scope class.

    As you can see this should be called when I type ‘(’ in my sources file. But it doesn’t happen.

     

    Tuesday, November 21, 2006 1:30 PM
  • Just want to note that I have overridden the ParseSource method in my LanguageService:

     

    public override AuthoringScope ParseSource(ParseRequest request)

    {

          if (request == null)

                throw new ArgumentNullException("request");

     

          Debug.Print(

                "File '{0}' ParseSource at ({1}:{2}), reason {3}, Timestamp {4}",

                Path.GetFileName(request.FileName),

                request.Line, request.Col, request.Reason, request.Timestamp);

     

          if (request.Sink.HiddenRegions)

          {

                ASLanguageServiceImpl.HiddenRegion(request);

          }

     

          return new CustomScope(request);

    }

     

    It‘s twice strange that GetMethods is not called because other overridden in CustomScope methods (like a GetDeclarations, GetDataTipText, Goto) are called perfect.

     

    Help! I need somebody's help...

    Wednesday, November 22, 2006 8:55 AM
  • This feature makes me crazy… J

    I have read the topic “Supporting IntelliSense Parameter Info (Managed Package Framework)” in VS SDK documentation. Especially interesting the last part of the topic named “Supporting the Parameter Info ToolTip in the Parser”. There are 4 steps that (as I understand) I have to do to support the method tips:

    The steps that the parser takes are outlined below:

    1.                 The parser calls the StartName method on the AuthoringSink object with the text "myfunc".

    2.                 The parser calls StartParameters on the AuthoringSink object.

    3.                 The parser calls NextParameter on the AuthoringSink object.

    4.                 The parser calls EndParameters on the AuthoringSink object.

    Does it mean that I have to call StartName StartParameters NextParameter and EndParameters methods myself in ParseSource method when request.Reason is ParseReason.MethodTip? It is not clear in documentation. 

    Could anybody tell me how to get the parameter info feature to work?

     

    Wednesday, November 22, 2006 12:14 PM
  • It looks like the missing step is setting the right trigger information in the Tokeninfo structure returned by the Scanner. Please look at the IronPython Sample for a demo on how to use the different triggers to start the parsing.

    /Paolo and Ole

    Wednesday, November 22, 2006 9:45 PM
  • Hi Paulo and Ole,

     

    Guys, I really wish it were that easy...

    Please, what else? Why this stuff is not working?

     

    That is my scanner code – ScanTokenAndProvideInfoAboutIt method:

     

    public bool ScanTokenAndProvideInfoAboutIt(TokenInfo tokenInfo, ref int state)

    {

                    SyntaxToken token = _tokenizer.GetNextToken(ref state);

     

                    if (tokenInfo == null ||token == null)

                    {

                                   return false;

                    }

                   

                    tokenInfo.Trigger = TokenTriggers.None;

     

                    switch(token.Type)

                    {

                                   case SyntaxType.EndOfFile:

                                                   return false;

     

                                   case SyntaxType.Keyword:

                                                   tokenInfo.Color = TokenColor.Keyword;

                                                   tokenInfo.Type = TokenType.Keyword;

                                                   break;

     

                                   case SyntaxType.Identifier:

                                                   tokenInfo.Color = TokenColor.Identifier;

                                                   tokenInfo.Type = TokenType.Identifier;

                                                   break;

                                                  

                                   case SyntaxType.LeftParenthesis:

                                                   tokenInfo.Color = TokenColor.Keyword;

                                                   tokenInfo.Type = TokenType.Delimiter;

                                                   tokenInfo.Trigger = TokenTriggers.MatchBraces | TokenTriggers.ParameterStart;

                                                   break;

                                                  

                                   case SyntaxType.RightParenthesis:

                                                   tokenInfo.Color = TokenColor.Keyword;

                                                   tokenInfo.Type = TokenType.Delimiter;

                                                   tokenInfo.Trigger = TokenTriggers.MatchBraces | TokenTriggers.ParameterEnd;

                                                   break;

                                                  

                                   case SyntaxType.Comma:

                                                   tokenInfo.Color = TokenColor.Keyword;

                                                   tokenInfo.Type = TokenType.Delimiter;

                                                   tokenInfo.Trigger = TokenTriggers.ParameterNext;

                                                   break;

                                                  

                                   case SyntaxType.OperatorDot:

                                                   tokenInfo.Color = TokenColor.Keyword;

                                                   tokenInfo.Type = TokenType.Operator;

                                                   tokenInfo.Trigger = TokenTriggers.MemberSelect;

                                                   break;

                                                  

                                   case SyntaxType.OperatorBrace:

                                                   tokenInfo.Color = TokenColor.Keyword;

                                                   tokenInfo.Type = TokenType.Operator;

                                                   tokenInfo.Trigger = TokenTriggers.MatchBraces;

                                                   break;

                                                  

                                   case SyntaxType.Operator:

                                                   tokenInfo.Color = TokenColor.Keyword;

                                                   tokenInfo.Type = TokenType.Operator;

                                                   break;

     

                                   case SyntaxType.MultiComment:

                                                   tokenInfo.Color = TokenColor.Comment;

                                                   tokenInfo.Type = TokenType.Comment;

                                                   break;

     

                                   case SyntaxType.SingleComment:

                                                   tokenInfo.Color = TokenColor.Comment;

                                                   tokenInfo.Type = TokenType.LineComment;

                                                   break;

     

                                   case SyntaxType.CharLiteral:

                                                   tokenInfo.Color = TokenColor.String;

                                                   tokenInfo.Type = TokenType.Literal;

                                                   break;

     

                                   case SyntaxType.IntLiteral:

                                                   tokenInfo.Color = TokenColor.Number;

                                                   tokenInfo.Type = TokenType.Literal;

                                                   break;

     

                                   case SyntaxType.StringLiteral:

                                                   tokenInfo.Color = TokenColor.String;

                                                   tokenInfo.Type = TokenType.String;

                                                   break;

     

                                   default:

                                                   tokenInfo.Color = TokenColor.Text;

                                                   tokenInfo.Type = TokenType.Text;

                                                   return false;

                    }

     

                    tokenInfo.Token = token.RawType;

                    tokenInfo.StartIndex = token.StartPosition.Col-1;

                    tokenInfo.EndIndex = token.EndPosition.Col-1;

     

                    return true;

    }

    Thursday, November 23, 2006 9:29 AM
  • ...wow! ...Dmitry, this is science fiction to me! As I said, I'm not using the MPF classes for my projects. I've developed my own classes that wrap the interop intarfaces and classes and I've added my own logic to them. Now you're asking me to get into an entirely different project model philosophy; that of the MPF classes.

        So, let's take it step by step please. I completely ignore all the code you've added here and, first of all, I'd like to note something on the heart of the issue as you describe it. You say that your GetMethods method is not called when you type "(" in the editor. Using general language-service logic, I believe that GetMethods should be called when you click "." and not "(". GetMethods is supposed to be used to populate the tool tip that allows the developer to choose one of many overloaded methods. By the time the developer has reached "(", the internal scanner must already know which overload the developer is editing. But as I said, I'm totally unaware of the logic used in the MPF classes.

    Saturday, November 25, 2006 2:24 PM
  • Hi Perikles,

     

    When I type dot the method GetDeclarations is called. MPF documentation tells that GetMethods is designed for Parameter Info Intellisense functionality. I was amazed too when reading the documentation. Unfortunately (or fortunately), I have to aware of the logic used in the MPF classes.

     

    It seems I’ve missed some important thing in the MPF. Well I still need some advice what exactly I’ve missed.

     

    Sorry if I bothered you with my email, Perikles. I’m not asking you to get into an entirely different project model philosophy – just I need help. If you can’t help me – never mind. Anyway, thank you for your advice. I appreciate it very much!

    Saturday, November 25, 2006 6:08 PM
  • Hello Dmitry,

    Did you have any luck with this? I'm seeing the exact thing.

    Thanks,

    John

     

     

    Saturday, December 16, 2006 5:49 PM
  • Hi John,

    As I understand we need to call these 4 methods - StartName, StartParameters, NextParameter, EndParameters - from our parsers. Managed Package Framework doesn't do it because it doesn't know how you parse the sources. Is it Yacc based parser or ANTLR or whatever else...

    See "Supporting IntelliSense Parameter Info (Managed Package Framework)" in VS SDK documentation about 4 methods I mentioned above.

     

    Monday, December 18, 2006 2:11 PM
  • Dmitry,

    Thanks for pointing me in the right direction! I'm using the MPPG (Yacc) parser and this worked perfect.

    Thanks,

    John

     

    Monday, December 18, 2006 6:34 PM
  • Hi Dimitry,

    are you still fighting this? If you are, here is my experience,  try to set a break point inside this method and remove all the other break points, see what happens.

    shiwei

    Tuesday, February 20, 2007 8:23 PM
  • Hi Shiwei,

    What method I should set the breakpoint inside?

    Tuesday, February 20, 2007 9:31 PM
  • the Getmethods method, which you overwrote in your derived AuthoringScope class.

     

    Wednesday, February 21, 2007 7:33 AM
  • Hi Shiwei,

    I did - nothing changed... :-)

    As we figured out - to break inside the GetMethod in my overriden from Authoring class I have to call 4 methods mentioned above in my parsing module. I mean these 4 methods: StartName, StartParameter, NextParameter and EndParameter.

    So what do you want to show me? Some kind of magic? :-)

    Wednesday, February 21, 2007 6:16 PM
  • Hi Dimitry, 

    I don't understand any more what happen inside your implementaiton. 

    If you called these 4 methods of Authoringsink class in your ParseSource method of your LanguageService class and return an instance of AuthoringScope class, the GetMethods should be called automatically.

     

    Shiwei

     

    Wednesday, February 21, 2007 6:33 PM
  • Hi Shiwei,

    Yes, you are right - if I call these methods - GetMethods will be called too.

    But when I started this topic I didn't call them. I'm using ANTLR instead of YACC for scaner / parser generation and I didn't know about I have to call these 4 methods myself.

    Now I know, I do and it works OK. So I think we should stop the discussion about that.

    But anyway thanx for your replies! :-) 

    Wednesday, February 21, 2007 10:50 PM
  • Hi Dmitry,
           I have this problem in my project.
    Could You give me some example where explicitly You call this 4 methods?
    I try use IronPhyton example but I am do not quite understand them.
    I try to use IronPhyton example but I am do not quite understand  where are these methods explicitly call.

    Best regards,
    Mykhaylo
    Saturday, September 12, 2009 6:24 PM
  • Hi Mykhaylo,

    I have implemented my custom class MyScanner which I inherited from Microsoft.VisualStudio.Package.IScanner interface:

    ... public class MyScanner : IScanner ...


    Then I overriden GetScanner method of my language service class:

        ...
        public override IScanner GetScanner(IVsTextLines buffer)
        {
            return new MyScanner(this, buffer);
        }
        ...

        
    So the method ScanTokenAndProvideInfoAboutIt (I mentioned it above in the thread) is an interface method you should implement.

    I also have replaced with custom standard MPF class AuthoringScope and I use it on overriden method od my language service class in following way:

            public override AuthoringScope ParseSource(ParseRequest request)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }
    
                string info = string.Format("File '{0}' ParseSource at ({1}:{2}), reason {3}, Timestamp {4}",
                                            Path.GetFileName(request.FileName),
                                            request.Line,
                                            request.Col,
                                            request.Reason,
                                            request.Timestamp);
                _logger.InfoFormat(info);
                Debug.Print(info);
    
                FeatureManager controller = new FeatureManager(this);
    
                request.Scope = new MyScope(request, this);
                try
                {
                    if (request.Sink.HiddenRegions)
                    {
                        controller.HandleHiddenRegion(request);
                    }
    
                    switch(request.Reason)
                    {
                        case ParseReason.Check:
                            controller.HandleSyntaxErrors(request);
                            controller.Reparse(request);
                            break;
                        case ParseReason.HighlightBraces:
                        case ParseReason.MatchBraces:
                            controller.HandleBraceMaching(request);
                            break;
                        case ParseReason.MethodTip:
                            controller.HandleMethodTip(request);
                            break;
                        default:
                            break;
                    }
                }
                catch(Exception e)
                {
                    _logger.Error("Error in ParseSource", e);
                }
                return request.Scope;
            }


    FeatureManager is my class I keep part of logic for handling some parse reasons in.

    And the rest of intellisense logic I keep in my authoring scope class.
    Dmitry [dimaka] Pavlov
    Sunday, September 13, 2009 4:49 PM
  • That is its code to help understand what it does:

        public class MyScope : AuthoringScope
        {
            #region Fields
    
            private static readonly ILog _logger = LogManager.GetLogger(typeof(MyScope));
            private readonly ParseRequest _request;
            private readonly MyLanguageService _service;
    
            #endregion
    
    
            #region Constructors
    
            public MyScope(ParseRequest request, MyLanguageService service)
            {
                _service = service;
                _request = request;
            }
    
            #endregion
    
    
            #region Public Methods
    
            public override string GetDataTipText(int line, int col, out TextSpan span)
            {
                _logger.Debug("GetDataTipText");
                SaveSourceFile();
    
                string tipText = string.Empty;
                span = new TextSpan();
    
                if (FeatureIsNotAvailable("Quick Info"))
                {
                    return null;
                }
    
                try
                {
                    string source = _request.Text;
                    string fileName = _request.FileName;
                    Position position = new Position(line + 1, col + 1);
    
                    SymbolTable cache = GetCache();
    
                    BaseDescriptor descriptor = cache.GetDescriptor(fileName, source, position);
    
                    if (descriptor != null)
                    {
                        SourceRange wordRange = descriptor.GetWordRange(fileName, position);
                        span = Utils.ConvertRangeToSpan(wordRange);
                        tipText = descriptor.ToString();
                        _service.WriteToStatusBar(tipText);
                    }
                    else
                    {
                        _service.WriteToStatusBar("Cannot evaluate descriptor.");
                    }
                }
                catch(Exception e)
                {
                    _logger.Error("Error in GetDataTipText", e);
                }
    
                _logger.DebugFormat("GetDataTipText [{0},{1}]: '{2}'", line, col, tipText);
                return tipText;
            }
    
            public override Declarations GetDeclarations(IVsTextView view,
                                                         int line,
                                                         int col,
                                                         TokenInfo info,
                                                         ParseReason reason)
            {
                _logger.Debug("GetDeclarations started");
                SaveSourceFile();
    
                // Stop intellisense if needed
                if (FeatureIsNotAvailable((reason == ParseReason.CompleteWord) ? "Word Completion" : "Member List"))
                {
                    return null;
                }
    
                try
                {
                    // Init variables
                    string source = _request.Text;
                    string fileName = _request.FileName;
                    Position position = new Position(line + 1, col + 1);
                    SymbolTable cache = GetCache();
    
                    if (MemberCompletionRequested(reason))
                    {
                        return new AsDeclarations(cache.GetMemberCompletion(fileName, source, position));
                    }
                    else
                    {
                        return new AsDeclarations(cache.GetWordCompletion(fileName, source, position));
                    }
                }
                catch(Exception e)
                {
                    _logger.Error("Error in GetDeclarations", e);
                }
    
                _logger.Debug("GetDeclarations completed");
                return null;
            }
    
            public override Methods GetMethods(int line, int col, string name)
            {
                _logger.DebugFormat("GetMethods [{0},{1}]: '{2}'", line, col, name);
                SaveSourceFile();
    
                if (FeatureIsNotAvailable("Parameter Info"))
                {
                    return null;
                }
    
                try
                {
                    string source = _request.Text;
                    string fileName = _request.FileName;
                    Position position = new Position(line + 1, col + 1);
                    SymbolTable cache = GetCache();
    
                    SourceRange wordRange;
                    MethodDescriptor method = cache.GetMethodDescriptor(fileName, source, position, out wordRange);
                    if (method == null)
                    {
                        _service.WriteToStatusBar("Cannot find appropriate method.");
                    }
                    else
                    {
                        _service.WriteToStatusBar(method.ToString());
                        _logger.Debug("GetMethods completed");
                        return new MyMethods(method, wordRange);
                    }
                }
                catch(Exception e)
                {
                    _logger.Error("Error in GetMethods", e);
                }
    
                _logger.Debug("GetMethods completed");
                return null;
            }
    
            public override string Goto(VSConstants.VSStd97CmdID cmd,
                                        IVsTextView textView,
                                        int line,
                                        int col,
                                        out TextSpan span)
            {
                _logger.DebugFormat("Goto [{0},{1}]: cmd '{2}'", line, col, cmd);
                SaveSourceFile();
    
                string uri = null;
                span = new TextSpan();
    
                if (FeatureIsNotAvailable("Go To Definition"))
                {
                    return null;
                }
    
                try
                {
                    string source = _request.Text;
                    string fileName = _request.FileName;
                    Position position = new Position(line + 1, col + 1);
                    SymbolTable cache = GetCache();
    
                    BaseDescriptor descriptor = cache.GetDescriptor(fileName, source, position);
                    if (descriptor != null)
                    {
                        ClassDescriptor klass = cache.FindClass(descriptor.ClassName);
                        uri = klass.FilePath;
                        span = Utils.ConvertRangeToSpan(descriptor.NameRange);
                    }
                    else
                    {
                        _service.WriteToStatusBar("Cannot find descriptor definition.");
                    }
                }
                catch(Exception e)
                {
                    _logger.Error("Error in Goto", e);
                }
                _logger.DebugFormat("Goto [{0},{1}]: '{2}'", line, col, uri);
                return uri;
            }
    
            #endregion
    
    
            #region Private Methods
    
            private SymbolTable GetCache()
            {
                return _service.GetCache(_request.FileName);
            }
    
            private void SaveSourceFile()
            {
                _service.SaveSourceFile(_request.FileName);
            }
    
            private bool FeatureIsNotAvailable(string featureName)
            {
                if (!SettingManager.EnableIntelliSense)
                {
                    string keyName = TextKeys.Message_Format_Feature_Switched_Off;
                    string message = string.Format(ResMgr.GetString(keyName), featureName);
                    _logger.Debug(message);
                    _service.WriteToStatusBar(message);
    
                    return true;
                }
                return false;
            }
    
            private static bool MemberCompletionRequested(ParseReason reason)
            {
                switch(reason)
                {
                    case ParseReason.CompleteWord:
                        return false;
                    case ParseReason.DisplayMemberList:
                    case ParseReason.MemberSelect:
                    case ParseReason.MemberSelectAndHighlightBraces:
                        return true;
                    default:
                        throw new ArgumentException("reason");
                }
            }
    
            #endregion
        }


    There are used some my classes like SymbolTable (it is the cache of syntax tree on the document), MethodDescriptor and other descriptors etc.

    Hope that will help you to win in the fighting with MPF language base classes :)

    Dmitry [dimaka] Pavlov
    Sunday, September 13, 2009 4:49 PM
  • Hi Dmitry,
          Thanks for Your answer. I have very similar source.
    But, I no idea where in this code You call this 4 methods.
    Please, if You can, tell me where You call this methods?

    Best regards,
    Mykhaylo
    Sunday, September 13, 2009 6:44 PM
  • Yes, Please!

    I have read this forum 2 or 3 times over and the solution remains a tease...NO ONE has posted where these 4 functions should be called, when, how and with what.

    Thanks.
    Friday, January 22, 2010 3:54 PM
  • Can you tell me where to call these 4 routines and under what circumstances? Nowhere in this thread is that made clear and it is rather frustrating that there are only 2 lines of discussion on this that can be found when i do a google search. They are also intertwined and neither spells it out. I realize this might cheat me out of the thrill of learning and discovery but I am willing to take that risk:-)

    Thanks,
    Sean
    Friday, January 22, 2010 8:36 PM
  • Hi Sean. I know the feeling.

     

    StartName, StartParameter, NextParameter, EndParameter need to be called from your grammar source file (the parser.y file that gets parsed by MPPG). They get called by the grammer productions that match the constraints of your specific language. Here is a section of my parser.y file that shows how this works for the Ada95 programming language. I'm sure you have similar productions in your grammar.

     

     

    name : simple_name   { $$ = $1; if (Sink.FindNames) Sink.StartName(MkTSpan(@1), $1.String); }
    	| indexed_comp    { $$ = $1; if (Sink.FindNames) Sink.StartName(MkTSpan(@1), $1.String); }
    	| selected_comp   { $$ = $1; if (Sink.FindNames) Sink.StartName(MkTSpan(@1), $1.String); }
    	| attribute
    	| operator_symbol
    	;
    
    indexed_comp : name
                   '('     { if (CurrentScope is SubprogBody) Sink.StartParameters(MkTSpan(@2)); }
                   value_s
                   ')'     { $$.String = $1.String;
                             if (CurrentScope is SubprogBody) Sink.EndParameters(MkTSpan(@5));
                             Match(@2, @5);  }
    	;
    
    
    value_s : value
    	| value_s 
    	  ','         { if (CurrentScope is SubprogBody) Sink.NextParameter(MkTSpan(@2)); }
    	  value
    	;

    This grammar fires StartParameters when a '(' token is typed, NextParameter when a ',' token is typed, and EndParameters when a ')' token is typed.

     

    The StartNames method is fired when the grammar comes across a method name as defined by the grammar. That part will look much different in your language.

     

    -Ben

     

     

     

    Sunday, January 31, 2010 12:42 AM
  • Hi Dmitry,

     

    In a member completion scenario, how do you know the identifier before '.' ??

    Sunday, May 09, 2010 10:55 AM