我正在开发一个VS Extension,它需要知道文本光标当前位于哪个类成员(方法,属性等)。它还需要了解 parent (例如类(class),嵌套类(class)等)。它需要知道成员或类的类型,名称和行号。当我说“类型”时,我的意思是“方法”或“属性”,不一定是“.NET类型”。

目前,我在这里使用此代码:

public static class CodeElementHelper
{
    public static CodeElement[] GetCodeElementAtCursor(DTE2 dte)
    {
        try
        {
            var cursorTextPoint = GetCursorTextPoint(dte);

            if (cursorTextPoint != null)
            {
                var activeDocument = dte.ActiveDocument;
                var projectItem = activeDocument.ProjectItem;
                var codeElements = projectItem.FileCodeModel.CodeElements;
                return GetCodeElementAtTextPoint(codeElements, cursorTextPoint).ToArray();
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine("[DBG][EXC] - " + ex.Message + " " + ex.StackTrace);
        }

        return null;
    }

    private static TextPoint GetCursorTextPoint(DTE2 dte)
    {
        var cursorTextPoint = default(TextPoint);

        try
        {
            var objTextDocument = (TextDocument)dte.ActiveDocument.Object();
            cursorTextPoint = objTextDocument.Selection.ActivePoint;
        }
        catch (Exception ex)
        {
            Debug.WriteLine("[DBG][EXC] - " + ex.Message + " " + ex.StackTrace);
        }

        return cursorTextPoint;
    }

    private static List<CodeElement> GetCodeElementAtTextPoint(CodeElements codeElements, TextPoint objTextPoint)
    {
        var returnValue = new List<CodeElement>();

        if (codeElements == null)
            return null;

        int count = 0;
        foreach (CodeElement element in codeElements)
        {
            if (element.StartPoint.GreaterThan(objTextPoint))
            {
                // The code element starts beyond the point
            }
            else if (element.EndPoint.LessThan(objTextPoint))
            {
                // The code element ends before the point
            }
            else
            {
                if (element.Kind == vsCMElement.vsCMElementClass ||
                    element.Kind == vsCMElement.vsCMElementProperty ||
                    element.Kind == vsCMElement.vsCMElementPropertySetStmt ||
                    element.Kind == vsCMElement.vsCMElementFunction)
                {
                    returnValue.Add(element);
                }

                var memberElements = GetCodeElementMembers(element);
                var objMemberCodeElement = GetCodeElementAtTextPoint(memberElements, objTextPoint);

                if (objMemberCodeElement != null)
                {
                    returnValue.AddRange(objMemberCodeElement);
                }

                break;
            }
        }

        return returnValue;
    }

    private static CodeElements GetCodeElementMembers(CodeElement codeElement)
    {
        CodeElements codeElements = null;

        if (codeElement is CodeNamespace)
        {
            codeElements = (codeElement as CodeNamespace).Members;
        }
        else if (codeElement is CodeType)
        {
            codeElements = (codeElement as CodeType).Members;
        }
        else if (codeElement is CodeFunction)
        {
            codeElements = (codeElement as CodeFunction).Parameters;
        }

        return codeElements;
    }
}

因此,这目前可行,如果我调用GetCodeElementAtCursor,我将获得该成员及其 parent 。 (这是一个很旧的代码,但是我相信我最初是从Carlos' blog捕获它并从VB移植的)。

我的问题是,当我的扩展名用在非常大的代码上时(例如,自动生成的文件有几千行),它会使VS爬行。几乎无法使用。运行探查器显示热线是
private static List<CodeElement> GetCodeElementAtTextPoint(CodeElements codeElements, TextPoint objTextPoint)
{
    foreach (CodeElement element in codeElements)
    {
        ...
/*-->*/ if (element.StartPoint.GreaterThan(objTextPoint)) // HERE <---
        {
            // The code element starts beyond the point
        }
/*-->*/ else if (element.EndPoint.LessThan(objTextPoint)) // HERE <----
        {
            // The code element ends before the point
        }
        else
        {
            ...
            var memberElements = GetCodeElementMembers(element);
/*-->*/     var objMemberCodeElement = GetCodeElementAtTextPoint(memberElements, objTextPoint); // AND, HERE <---

            ...
        }
    }

    return returnValue;
}

因此,第三个显而易见,它是对自身的递归调用,因此,影响它的任何因素都会影响对其自身的调用。但是,前两个我不确定如何解决。
  • 是否可以使用其他方法来检索光标所在的成员的类型(类,方法,属性等),名称,行号和父级?
  • 是否可以做一些事情来使TextPoint.GreaterThanTestPoint.LessThan方法的性能更好?
  • 或者我是S.O.L.?

  • 无论采用哪种方法,都只需要支持VS2015或更高版本即可。

    谢谢!

    更新:要回答Sergey的评论-确实确实是由.GreaterThan/.LessThan()引起的。我已经分离了代码,并且减速肯定是在这些方法调用上发生的,而不是element.StartPointelement.EndPoint的属性访问器。

    c# - VS扩展: TextPoint.大于/小于对于大型文件来说非常慢-LMLPHP

    最佳答案

    使用GetCursorTextPoint获得TextPoint之后,可以使用TextPoint.CodeElement属性查找当前代码元素:

        EnvDTE.TextPoint p = GetCursorTextPoint(DTE);
        foreach (EnvDTE.vsCMElement i in Enum.GetValues(typeof(EnvDTE.vsCMElement)))
        {
            EnvDTE.CodeElement e = p.CodeElement[i];
            if (e != null)
                System.Windows.MessageBox.Show(i.ToString() + " " + e.FullName);
        }
    

    关于c# - VS扩展: TextPoint.大于/小于对于大型文件来说非常慢,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45359130/

    10-13 06:02