88 votes

Comment l'achèvement du code du travail?

Beaucoup de rédacteurs en chef et les IDEs ont la complétion de code. Certains d'entre eux sont très "intelligent" autres ne le sont pas vraiment. Je suis intéressé dans les plus intelligents de type. Par exemple, j'ai vu des IDEs qui ne proposent qu'une fonction si elle est disponible dans le courant de la portée b) sa valeur de retour est valable. (Par exemple, après "5 + foo[tab]" il ne propose que les fonctions qui renvoient à quelque chose qui peut être ajouté à un nombre entier ou des noms de variables du type correct.) J'ai également vu qu'ils ont lieu le plus souvent utilisé ou la plus longue option à l'avance de la liste.

Je me rends compte que vous devez analyser le code. Mais généralement, lors de l'édition de l'actuel code n'est pas valide il y a des erreurs de syntaxe. Comment analyser quelque chose quand il est incomplet et qu'il contient des erreurs?

Il y a aussi une contrainte de temps. La fin est inutile si elle ne prend que quelques secondes à venir avec une liste. Parfois, l'achèvement de l'algorithme traite avec des milliers de classes.

Quels sont les bons algorithmes et structures de données pour cela?

68voto

280Z28 Points 49515

Le moteur IntelliSense dans mon UnrealScript langue de service produit est compliqué, mais je vais vous donner un meilleur aperçu ici que je peux. Le service de langage C# dans VS2008 SP1 est mon objectif de performance (pour de bonnes raisons). Il n'y est pas encore, mais il est rapide/précis que je peux vous offrir des suggestions après un seul caractère est tapé, sans attendre les touches ctrl+espace ou l'utilisateur de taper un . (dot). Le plus d'informations que les gens [de travail sur les services de langue] obtenir sur ce sujet, la meilleure expérience de l'utilisateur final je obtenir devrais-je jamais faire usage de leurs produits. Il y a un certain nombre de produits que j'ai eu la malheureuse expérience de travail avec qui n'a pas payer une telle attention aux détails, et comme un résultat, je me battais avec l'IDE que j'étais de codage.

Dans ma langue de service, c'est énoncée comme suit:

  1. Obtenir l'expression au niveau du curseur. Ce guide depuis le début de l' accès des membres de l'expression à la fin de l'identifiant que le curseur est sur. L'accès des membres expression est généralement sous la forme aa.bb.cc, mais peut aussi contenir des appels de méthode comme en aa.bb(3+2).cc.
  2. Obtenez le contexte entourant le curseur. C'est très délicat, car il n'est pas toujours suivre les mêmes règles que le compilateur (longue histoire), mais pour ici supposer qu'elle ne. Cela signifie généralement obtenir les informations mises en cache sur la méthode/classe le curseur est à l'intérieur.
  3. Dire le contexte, l'objet implémente IDeclarationProvider, où vous pouvez appeler l' GetDeclarations() pour obtenir un IEnumerable<IDeclaration> de tous les éléments visibles dans le champ. Dans mon cas, cette liste contient les variables locales/les paramètres (si dans une méthode), les membres (champs et méthodes statiques uniquement, sauf dans une méthode d'instance, et aucun des membres privés de types de base), globals (types et les constantes de la langue, je travaille sur), et les mots clés. Dans cette liste seront un élément avec le nom aa. Comme une première étape dans l'évaluation de l'expression dans le #1, nous avons sélectionnez l'élément dans le contexte de l'énumération avec le nom aa, ce qui nous donne un IDeclaration pour la prochaine étape.
  4. Ensuite, j'applique l'opérateur à l' IDeclaration représentant aa pour obtenir un autre IEnumerable<IDeclaration> contenant les "membres" (dans un sens) aa. Depuis l' . opérateur est différent de l' -> opérateur, j'appelle declaration.GetMembers(".") et s'attendre à l' IDeclaration objet pour appliquer correctement la liste de l'opérateur.
  5. Cela continue jusqu'à ce que je frappe cc, où la déclaration de la liste peut ou peut ne pas contenir un objet avec le nom de l' cc. Je suis sûr que vous êtes au courant, si plusieurs éléments de commencer avec cc, ils devraient apparaître. - Je résoudre ce problème en prenant la finale de l'énumération et de la faire passer dans mon documenté algorithme de fournir à l'utilisateur avec les renseignements les plus utiles possible.

Voici quelques remarques supplémentaires pour l'IntelliSense backend:

  • J'ai la grande étendue de l'utilisation de LINQ paresseux mécanismes d'évaluation dans la mise en oeuvre GetMembers. Chaque objet dans le cache de mon navigateur est en mesure de fournir un foncteur qui donne à ses membres, de sorte que l'exécution d'actions complexes avec l'arbre est près de trivial.
  • À la place de chaque objet en maintenant un List<IDeclaration> de ses membres, je garde un List<Name>Name est une structure contenant le hash d'un spécialement formaté chaîne de caractères décrivant le membre. Il y a une énorme cache des cartes des noms aux objets. De cette façon, quand je me suis ré-analyser un fichier, je peux supprimer tous les éléments déclarés dans le fichier à partir du cache et de la repeupler avec les membres mis à jour. En raison de la façon dont les foncteurs sont configurés, toutes les expressions d'évaluer immédiatement pour les nouveaux éléments.

IntelliSense "frontend"

Comme les types d'utilisateur, le fichier est syntaxiquement incorrect le plus souvent il est correct. En tant que tel, je ne veux pas par hasard supprimer des sections de cache lorsque l'utilisateur tape. J'ai un grand nombre de cas à des règles en place pour gérer les mises à jour incrémentielles aussi rapidement que possible. L'augmentation du cache est conservée le local ouvrir un fichier et aide à faire en sorte que l'utilisateur ne se rend pas compte que leur typage est à l'origine du backend de cache pour tenir incorrecte de la ligne/colonne d'informations pour des choses comme chaque méthode dans le fichier.

  • Un facteur de rachat est mon analyseur est rapide. Il peut gérer une complète mise à jour du cache de 20000 ligne du fichier source dans 150ms en fonctionnement autonome sur une faible priorité de thread d'arrière-plan. Chaque fois que cet analyseur effectue une passe sur un fichier ouvert avec succès (la syntaxe), l'état actuel du fichier est déplacé dans le cache global.
  • Si le fichier n'est pas syntaxiquement correct, j'utilise un ANTLR filtre analyseur (désolé pour le lien - info est sur la liste de diffusion ou recueillis à partir de la lecture de la source) pour analyse le fichier à la recherche:
    • Variable/champ déclarations.
    • La signature de class/struct définitions.
    • La signature de la méthode définitions.
  • Dans le cache local, class/struct/méthode définitions de commencer à la signature et à la fin quand le corset niveau d'imbrication remonte même. Les méthodes peuvent également fin si une autre méthode de déclaration est atteint (pas de nidification des méthodes).
  • Dans le cache local, les variables et les champs sont liés à la précède immédiatement ouverte de l'élément. Voir le bref extrait de code ci-dessous pour un exemple de pourquoi c'est important.
  • Aussi, comme les types d'utilisateurs, je garde une remap tableau de marquage de l'ajoutés/supprimés les plages de caractères. Ce est utilisé pour:
    • Faire en sorte que je peux identifier le bon contexte du curseur, depuis une méthode peut/veut se déplacer dans le fichier entre l'analyse.
    • Faire assurez-vous Aller À Déclaration/Définition/Référence localise les éléments correctement dans les fichiers ouverts.

Extrait de Code pour la section précédente:

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

J'ai pensé ajouter une liste de fonctionnalités IntelliSense j'ai mis en œuvre cette disposition. Des photos de chacun sont situés ici.

  • L'Auto-complétion
  • Outil de conseils
  • Des Conseils De Méthode
  • Point De Vue De Classe
  • La Définition Du Code De La Fenêtre
  • Appel du Navigateur (VS 2010 enfin il ajoute cela à C#)
  • Sémantiquement correcte de Trouver Toutes les Références

17voto

Adam Rosenfield Points 176408

Je ne peux pas dire exactement quels algorithmes sont utilisés par un particulier mise en œuvre, mais je peux faire des suppositions éclairées. Un trie est très utile structure de données pour résoudre ce problème: l'IDE peut maintenir un grand trie dans la mémoire de tous les symboles de votre projet, avec certains de métadonnées supplémentaires à chaque nœud.

Lorsque vous tapez un caractère, il se promène sur un chemin dans le trie. Tous les descendants d'un trie de nœud sont complétions possibles. L'IDE suffit alors de filtre par ceux qui ont un sens dans le contexte actuel, mais il suffit de calculer autant que peut être affichée dans l'onglet de l'achèvement de la fenêtre pop-up.

Plus d'onglet avancé-l'achèvement requiert une plus compliqué trie. Par exemple, Visual Assist X dispose d'une fonction par laquelle vous avez seulement besoin de taper les majuscules CamelCase symboles -- par exemple, si vous tapez SFN, il vous montre le symbole SomeFunctionName dans son onglet de l'achèvement de la fenêtre.

Le calcul de la trie (ou d'autres structures de données) nécessite l'analyse l'ensemble de votre code pour obtenir une liste de tous les symboles de votre projet. Visual Studio enregistre dans ses IntelliSense de base de données, un .ncb fichier stocké le long de votre projet, de sorte qu'il n'a pas à l'analyse de tout, à chaque fois que vous fermez et rouvrez votre projet. La première fois que vous ouvrez un projet de grande envergure (par exemple, celui que vous venez de synchronisés forme de contrôle de la source), contre la volonté de prendre le temps d'analyser tout et de générer la base de données.

Je ne sais pas comment il gère les modifications incrémentielles. Comme vous l'avez dit, lorsque vous écrivez du code, c'est de la syntaxe invalide à 90% du temps, et ré-tout quand vous le ralenti entraînerait un énorme de l'impôt sur votre CPU pour très peu d'avantages, surtout si vous êtes à la modification d'un fichier d'en-tête inclus par un grand nombre de fichiers source.

Je soupçonne qu'il soit (un) seulement reparses chaque fois que vous construisez votre projet (ou peut-être quand vous fermer/ouvrir), ou (b) il fait un peu de tri des locaux de l'analyse où il ne traite le code autour de l'endroit où vous avez juste édité dans certains limité de la mode, juste pour obtenir les noms des symboles pertinents. Depuis C++ a une telle extraordinairement compliqué de grammaire, il peut se comporter bizarrement dans les coins sombres si vous êtes en utilisant un gros modèle de la métaprogrammation et la comme.

1voto

matrix Points 51

Le lien suivant vous aider..

La coloration syntaxique:Rapide Couleur de zone de texte pour la coloration syntaxique

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X