Pregunta

Muchos de los editores e IDEs tienen la finalización de código.Algunos de ellos son muy "inteligentes" que otros no lo son realmente.Estoy interesado en el más inteligente de tipo.Por ejemplo he visto IDEs que sólo ofrecen una función si es una) disponible en el actual ámbito de aplicación b) su valor de retorno es válido.(Por ejemplo después de "5 + foo[tab]" sólo ofrece funciones que devuelven algo que puede ser añadido a un número entero o los nombres de variable del tipo correcto.) También he visto que tiene lugar la utiliza más a menudo o más largo de la opción por delante de la lista.

Me doy cuenta de que usted necesita para analizar el código.Pero normalmente, mientras que la edición actual del código no es válido, hay errores de sintaxis en ella.¿Cómo se puede analizar algo cuando es incompleta y contiene errores?

También hay una restricción de tiempo.La conclusión es inútil si se toma unos segundos para venir para arriba con una lista.A veces el algoritmo de finalización de ofertas con miles de clases.

¿Cuáles son los buenos algoritmos y estructuras de datos para esto?

¿Fue útil?

Solución

El motor de IntelliSense en mi UnrealScript idioma servicio del producto es complicado, pero voy a dar lo mejor un resumen aquí, como puedo.El lenguaje C# servicio en VS2008 SP1 es mi objetivo de rendimiento (por buenas razones).No lo hay, pero es rápido y lo suficientemente preciso que puedo ofrecer sugerencias después de un solo carácter se escribe, sin esperar ctrl+espacio o el usuario escribe una . (dot, en inglés).Más información de las personas que trabajan en servicios de idiomas] obtener acerca de este tema, la mejor experiencia del usuario final puedo obtener si alguna vez puedo usar sus productos.Hay una serie de productos que he tenido la desafortunada experiencia de trabajo con los que no prestar mucha atención a los detalles, y como resultado me estaba peleando con el IDE más que yo de codificación.

En mi idioma servicio, que se presentan como las siguientes:

  1. Obtener la expresión en el cursor.Esta paseos desde el comienzo de la acceso de los miembros de expresión al final del identificador se encuentra el cursor.El acceso de los miembros de expresión es generalmente en la forma aa.bb.cc, pero también puede contener llamadas a métodos como en aa.bb(3+2).cc.
  2. Obtener la contexto rodea el cursor.Esto es muy complicado, porque no siempre siguen las mismas reglas que el compilador (larga historia), pero por aquí se supone que hace.En general, esto significa obtener la información almacenada en caché sobre el método o clase el cursor está dentro.
  3. Dicen que el objeto de contexto que se implementa IDeclarationProvider, donde se puede llamar a GetDeclarations() para obtener una IEnumerable<IDeclaration> de todos los elementos visibles en el ámbito de aplicación.En mi caso, esta lista contiene los lugareños/parámetros (si en un método), miembros (campos y métodos de la estática, a menos que en un método de instancia, y no los miembros privados de la base de tipos), globales (tipos y constantes para el idioma en el que estoy trabajando), y las palabras clave.En esta lista será un elemento con el nombre de aa.Como un primer paso en la evaluación de la expresión en el #1, se selecciona el elemento en el contexto de la enumeración con el nombre de aa, que nos da una IDeclaration para el siguiente paso.
  4. A continuación, aplicar el operador a la IDeclaration en representación de aa para conseguir otro IEnumerable<IDeclaration> contiene los "miembros" (en algún sentido) de aa.Desde el . el operador es diferente de la -> el operador, que me llame declaration.GetMembers(".") y esperar que el IDeclaration objeto de aplicar correctamente la lista de operador.
  5. Esto continúa hasta que me golpeó cc, donde la declaración de la lista de puede o no puede contienen un objeto con el nombre de cc.Como estoy seguro de que eres consciente, si hay varios elementos de comenzar con cc, que debería aparecer así.Voy a resolver esto mediante la adopción de la final de la enumeración y pasar a través de mi documentado algoritmo para proporcionar al usuario la información más útil posible.

Aquí están algunas notas adicionales para el IntelliSense backend:

  • Me hacen un amplio uso de LINQ perezoso mecanismos de evaluación en la aplicación de GetMembers.Cada objeto en mi memoria caché es capaz de proporcionar un functor que evalúa a sus miembros, a fin de realizar acciones complicadas con el árbol está cerca de trivial.
  • En lugar de que cada objeto de mantener un List<IDeclaration> de sus miembros, guardo una List<Name>, donde Name es una estructura que contiene el hash de un formato especial de cadena que describe el miembro.Hay una enorme caché que se asigna nombres a los objetos.De esta manera, cuando puedo volver a analizar un archivo, me pueden quitar todos los elementos declarados en el archivo de la caché y llenarlo con la versión actualizada de los miembros.Debido a la forma en que el functors están configurados, todas las expresiones evaluar de inmediato a los nuevos elementos.

IntelliSense "frontend"

Como los tipos de usuario, el archivo es sintácticamente incorrecta más a menudo de lo que es correcto.Como tal, no quiero al azar quitar secciones de la memoria caché cuando el usuario escribe.Tengo un gran número de casos especiales reglas para manejar las actualizaciones incrementales tan pronto como sea posible.El incremento en la caché de sólo mantuvo local a un archivo abierto y permite asegurar que el usuario no se da cuenta de que su tipificación está causando el backend de caché para mantener incorrecto línea/columna de información para cosas como cada método en el archivo.

  • Un punto a su favor el factor es mi parser es rápido.Puede manejar una completa actualización de la memoria caché de una 20000 línea de archivo de origen en 150ms mientras que la operación de auto-contenida en una prioridad baja subproceso en segundo plano.Cuando este analizador completa un pase en un archivo abierto con éxito (sintácticamente), el estado actual de que el archivo se mueve en la caché global de ensamblados.
  • Si el archivo no es sintácticamente correcto, yo uso un ANTLR filtro analizador (lo siento por el enlace - la mayoría de la información está en la lista de correo o recopilada a partir de la lectura de la fuente) para reanálisis el archivo buscando:
    • Variable/campo de declaraciones.
    • La firma para la clase/struct definiciones.
    • La firma de las definiciones de método.
  • En la caché local, clase/struct/definiciones de método de comenzar en la firma y al final cuando la llave de nivel de anidamiento se remonta incluso.Los métodos también puede terminar si otra declaración del método que se alcanza (no de anidación de métodos).
  • En la caché local, variables o campos están vinculados a la inmediatamente anterior no cerradas elemento.Ver la breve fragmento de código a continuación un ejemplo de por qué esto es importante.
  • También, como los tipos de usuario, tengo una reasignación de la tabla marcando el añadido/quitado rangos de caracteres.Este es utilizado para:
    • Asegurarse de que puede identificar el contexto correcto del cursor, ya que un método puede/no se mueven en el archivo entre los analiza.
    • Asegurándose de que Vaya A la Declaración/Definición/Referencia localiza los elementos correctamente en los archivos abiertos.

Fragmento de código para la sección anterior:

class A
{
    int x; // linked to A

    void foo() // linked to A
    {
        int local; // linked to foo()

    // foo() ends here because bar() is starting
    void bar() // linked to A
    {
        int local2; // linked to bar()
    }

    int y; // linked again to A

Pensé que había que añadir una lista de las características de IntelliSense he implementado con este diseño. Fotos de cada uno se encuentra aquí.

  • Auto-completar
  • Herramienta de consejos
  • Método De Consejos
  • Vista De La Clase
  • Definición De Código De La Ventana
  • El Examinador de la llamada (VS 2010 por último se añade este a C#)
  • Semánticamente correcto Buscar Todas las Referencias

Otros consejos

No puedo decir exactamente qué algoritmos utiliza una implementación en particular, pero puedo hacer algunas conjeturas. Un trie es una estructura de datos muy útil para este problema: el IDE puede mantener un gran trie en la memoria de todos los símbolos en su proyecto, con algunos metadatos adicionales en cada nodo.

Cuando escribes un personaje, camina por un camino en el trie. Todos los descendientes de un nodo trie particular son posibles terminaciones. El IDE solo necesita filtrarlos por los que tienen sentido en el contexto actual, pero solo necesita calcular tantos como se pueden mostrar en la ventana emergente de finalización de pestaña.

La finalización de pestañas más avanzada requiere un proceso más complicado. Por ejemplo, Visual Assist X tiene una función por la que solo necesita escribir las letras mayúsculas de los símbolos CamelCase, por ejemplo , si escribe SFN, le muestra el símbolo SomeFunctionName en su ventana de finalización de tabulación.

Calcular el trie (u otras estructuras de datos) requiere analizar todo su código para obtener una lista de todos los símbolos en su proyecto. Visual Studio almacena esto en su base de datos IntelliSense, un archivo .ncb almacenado junto con su proyecto, para que no tenga que volver a analizar todo cada vez que cierre y vuelva a abrir su proyecto. La primera vez que abra un proyecto grande (digamos, uno que acaba de sincronizar desde el control de fuente), VS se tomará el tiempo de analizar todo y generar la base de datos.

No sé cómo maneja los cambios incrementales. Como dijiste, cuando escribes código, es una sintaxis no válida el 90% del tiempo, y volver a analizar todo cuando estás inactivo supondría un gran impuesto para tu CPU por muy poco beneficio, especialmente si estás modificando un archivo de encabezado incluido por una gran cantidad de archivos fuente.

Sospecho que (a) solo se analiza cada vez que realmente construyes tu proyecto (o posiblemente cuando lo cierras / abres), o (b) realiza algún tipo de análisis local donde solo analiza el código donde Acabo de editar de forma limitada, solo para obtener los nombres de los símbolos relevantes. Dado que C ++ tiene una gramática tan extraordinariamente complicada, puede comportarse de manera extraña en las esquinas oscuras si está utilizando metaprogramación de plantillas pesadas y similares.

El siguiente enlace lo ayudará aún más ...

Resaltado de sintaxis: TextBox de color rápido para resaltado de sintaxis

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top