سؤال

الكثير من المحررين وIDEs لديهم إكمال التعليمات البرمجية.بعضهم "أذكياء" للغاية والبعض الآخر ليس كذلك حقًا.أنا مهتم بالنوع الأكثر ذكاءً.على سبيل المثال، رأيت بيئات التطوير المتكاملة (IDE) التي تقدم وظيفة فقط إذا كانت أ) متوفرة في النطاق الحالي ب) قيمة الإرجاع الخاصة بها صالحة.(على سبيل المثال، بعد "5 + foo[tab]" فإنه يقدم فقط الوظائف التي تُرجع شيئًا يمكن إضافته إلى عدد صحيح أو أسماء متغيرات من النوع الصحيح.) لقد رأيت أيضًا أنهم يضعون الخيار الأكثر استخدامًا أو الأطول في المقدمة. من القائمة.

أدرك أنك بحاجة إلى تحليل الكود.ولكن عادةً ما تكون هناك أخطاء في بناء الجملة عندما يكون تحرير الكود الحالي غير صالح.كيف يمكنك تحليل شيء ما عندما يكون غير مكتمل ويحتوي على أخطاء؟

هناك أيضا قيود زمنية.الإكمال عديم الفائدة إذا استغرق الأمر ثوانٍ للتوصل إلى قائمة.في بعض الأحيان تتعامل خوارزمية الإكمال مع آلاف الفئات.

ما هي الخوارزميات وهياكل البيانات الجيدة لهذا؟

هل كانت مفيدة؟

المحلول

يعد محرك IntelliSense في منتج خدمة اللغة UnrealScript معقدًا، ولكنني سأقدم أفضل نظرة عامة هنا قدر الإمكان.إن خدمة لغة C# في VS2008 SP1 هي هدف الأداء الخاص بي (لسبب وجيه).لم يتم الوصول إليها بعد، ولكنها سريعة/دقيقة بدرجة كافية حتى أتمكن من تقديم اقتراحات بأمان بعد كتابة حرف واحد، دون انتظار ctrl+space أو قيام المستخدم بكتابة . (نقطة).كلما حصل الأشخاص [الذين يعملون في الخدمات اللغوية] على مزيد من المعلومات حول هذا الموضوع، كلما حصلت على تجربة أفضل للمستخدم النهائي إذا استخدمت منتجاتهم.هناك عدد من المنتجات التي مررت بتجربة مؤسفة في العمل معها والتي لم تهتم كثيرًا بالتفاصيل، ونتيجة لذلك كنت أقاتل مع IDE أكثر مما كنت أقوم بالبرمجة.

في خدمة اللغة الخاصة بي، تم وضعها على النحو التالي:

  1. احصل على التعبير عند المؤشر.هذا يمشي من بداية تعبير وصول الأعضاء إلى نهاية المعرف انتهى المؤشر.يكون تعبير وصول الأعضاء بشكل عام في النموذج aa.bb.cc, ، ولكن يمكن أن تحتوي أيضًا على استدعاءات الطريقة كما في aa.bb(3+2).cc.
  2. احصل على ال سياق المحيطة بالمؤشر.هذا أمر صعب للغاية، لأنه لا يتبع دائمًا نفس القواعد التي يتبعها المترجم (قصة طويلة)، ولكن هنا افترض أنه يفعل ذلك.بشكل عام، يعني هذا الحصول على المعلومات المخزنة مؤقتًا حول الطريقة/الفئة التي يوجد بها المؤشر.
  3. لنفترض أن كائن السياق ينفذ IDeclarationProvider, ، حيث يمكنك الاتصال GetDeclarations() للحصول على IEnumerable<IDeclaration> لجميع العناصر المرئية في النطاق.في حالتي، تحتوي هذه القائمة على الإعدادات المحلية/المعلمات (إذا كانت في إحدى الطرق)، والأعضاء (الحقول والأساليب، الثابتة فقط ما لم تكن في طريقة مثيل، ولا يوجد أعضاء خاصون من الأنواع الأساسية)، والعناصر العامة (الأنواع والثوابت للغة التي أستخدمها). أعمل على)، والكلمات الرئيسية.في هذه القائمة سيكون هناك عنصر بالاسم aa.كخطوة أولى في تقييم التعبير في رقم 1، نختار العنصر من تعداد السياق بالاسم aa, ، مما يتيح لنا IDeclaration للخطوة التالية.
  4. بعد ذلك، أقوم بتطبيق عامل التشغيل على IDeclaration يمثل aa للحصول على آخر IEnumerable<IDeclaration> تحتوي على "أعضاء" (بمعنى ما) لـ aa.منذ . المشغل يختلف عن -> المشغل، أتصل declaration.GetMembers(".") وتوقع IDeclaration كائن لتطبيق عامل التشغيل المدرج بشكل صحيح.
  5. يستمر هذا حتى أضرب cc, ، حيث قائمة الإعلان يمكن اه ويمكن لا تحتوي على كائن بالاسم cc.وأنا متأكد من أنك على علم، إذا كانت العناصر المتعددة تبدأ بـ cc, ، ينبغي أن تظهر كذلك.أقوم بحل هذه المشكلة عن طريق أخذ التعداد النهائي وتمريره الخوارزمية الموثقة الخاصة بي لتزويد المستخدم بالمعلومات الأكثر فائدة الممكنة.

فيما يلي بعض الملاحظات الإضافية لواجهة IntelliSense الخلفية:

  • لقد استخدمت على نطاق واسع آليات التقييم البطيئة الخاصة بـ LINQ في التنفيذ GetMembers.كل كائن في ذاكرة التخزين المؤقت الخاصة بي قادر على توفير عامل يتم تقييمه لأعضائه، لذا فإن تنفيذ إجراءات معقدة باستخدام الشجرة أمر تافه تقريبًا.
  • بدلاً من الاحتفاظ بكل كائن بـ a List<IDeclaration> من أعضائها، وأحتفظ ب List<Name>, ، أين Name عبارة عن بنية تحتوي على تجزئة سلسلة منسقة خصيصًا تصف العضو.هناك ذاكرة تخزين مؤقت هائلة تقوم بتعيين الأسماء للكائنات.بهذه الطريقة، عندما أقوم بإعادة تحليل ملف، يمكنني إزالة كافة العناصر المعلنة في الملف من ذاكرة التخزين المؤقت وإعادة ملؤه بالأعضاء المحدثين.نظرًا للطريقة التي تم بها تكوين العوامل الوظيفية، يتم تقييم جميع التعبيرات على الفور وفقًا للعناصر الجديدة.

التحسس الذكي "الواجهة الأمامية"

أثناء قيام المستخدم بالكتابة، يكون الملف من الناحية النحوية غير صحيح في كثير من الأحيان مما هو صحيح.على هذا النحو، لا أريد إزالة أقسام من ذاكرة التخزين المؤقت بشكل عشوائي عندما يكتب المستخدم.لدي عدد كبير من قواعد الحالات الخاصة للتعامل مع التحديثات الإضافية في أسرع وقت ممكن.يتم الاحتفاظ بذاكرة التخزين المؤقت المتزايدة محليًا فقط في ملف مفتوح وتساعد على التأكد من أن المستخدم لا يدرك أن كتابته تتسبب في احتفاظ ذاكرة التخزين المؤقت الخلفية بمعلومات سطر/عمود غير صحيحة لأشياء مثل كل طريقة في الملف.

  • أحد عوامل الاسترداد هو المحلل اللغوي الخاص بي سريع.يمكنه التعامل مع تحديث ذاكرة التخزين المؤقت الكامل لملف مصدر مكون من 20000 سطر في 150 مللي ثانية أثناء التشغيل بشكل مستقل على مؤشر ترابط خلفية ذو أولوية منخفضة.عندما يكمل هذا المحلل تمريرة على ملف مفتوح بنجاح (نحويًا)، يتم نقل الحالة الحالية للملف إلى ذاكرة التخزين المؤقت العامة.
  • إذا لم يكن الملف صحيحًا من الناحية النحوية، فأنا أستخدم ملف محلل مرشح ANTLR (نأسف على الرابط - معظم المعلومات موجودة في القائمة البريدية أو تم جمعها من قراءة المصدر) لإعادة تحليل الملف الذي يبحث عن:
    • إعلانات المتغير/الحقل.
    • التوقيع لتعريفات الفئة/البنية.
    • التوقيع لتعريفات الطريقة.
  • في ذاكرة التخزين المؤقت المحلية، تبدأ تعريفات الفئة/البنية/الطريقة عند التوقيع وتنتهي عندما يعود مستوى تداخل الأقواس إلى المستوى الزوجي.يمكن أن تنتهي الطرق أيضًا إذا تم الوصول إلى تعريف طريقة أخرى (بدون طرق تداخل).
  • في ذاكرة التخزين المؤقت المحلية، ترتبط المتغيرات/الحقول بما يسبقها مباشرة مفتوح عنصر.راجع مقتطف الكود المختصر أدناه للحصول على مثال عن سبب أهمية ذلك.
  • أيضًا، أثناء قيام المستخدم بالكتابة، أحتفظ بجدول إعادة رسم خريطة لتحديد نطاقات الأحرف المضافة/المحذوفة.يستخدم هذا ل:
    • التأكد من أنني أستطيع تحديد السياق الصحيح للمؤشر، حيث يمكن/تتحرك الطريقة في الملف بين التحليلات الكاملة.
    • التأكد من أن الانتقال إلى الإعلان/التعريف/المرجع يحدد موقع العناصر بشكل صحيح في الملفات المفتوحة.

مقتطف الكود للقسم السابق:

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

اعتقدت أنني سأضيف قائمة بميزات IntelliSense التي قمت بتنفيذها باستخدام هذا التخطيط. توجد صور لكل منها هنا.

  • الإكمال التلقائي
  • نصائح حول الأداة
  • نصائح الطريقة
  • عرض الفصل
  • نافذة تعريف الكود
  • متصفح الاتصال (VS 2010 يضيف هذا أخيرًا إلى C#)
  • البحث الصحيح لغويًا عن كافة المراجع

نصائح أخرى

لا أستطيع أن أقول بالضبط ما تستخدم خوارزميات من قبل أي تطبيق معين، لكنني يمكن أن تجعل بعض التخمينات. A TRIE هو بنية بيانات مفيدة جدا لهذه المشكلة: في IDE يمكن الحفاظ على TRIE كبير في الذاكرة من كل حرف في المشروع، مع بعض بيانات تعريف إضافية في كل عقدة.

عند كتابة حرف، فإنه يمشي في طريق في TRIE. كل من أحفاد عقدة TRIE معينة هي الاكمال الممكنة. وIDE ثم يحتاج فقط لتصفية تلك من قبل تلك التي يكون له معنى في السياق الحالي، ولكنها تحتاج فقط لحساب العديد كما يمكن أن يتم عرضها في نافذة منبثقة التبويب الانتهاء.

وأكثر تقدما التبويب الإنجاز يتطلب TRIE أكثر تعقيدا. على سبيل المثال، البصرية مساعدة X لديه ميزة حيث تحتاج فقط إلى كتابة حروف الرموز CamelCase - على سبيل المثال ، إذا قمت بكتابة SFN، فإنه يظهر لك SomeFunctionName الرمز في نافذة التبويب انتهائها.

والحوسبة وTRIE (أو هياكل البيانات الأخرى) لا يتطلب تحليل كل من التعليمات البرمجية الخاصة بك للحصول على قائمة بكافة رموز في المشروع. البصرية ستوديو بتخزين هذه في قاعدة بيانات التحسس لها، ملف .ncb المخزنة جنبا إلى جنب مع المشروع الخاص بك، بحيث لا تضطر إلى إعادة التوزيع كل شيء في كل مرة تقوم بإغلاق وفتح المشروع الخاص بك. في المرة الأولى التي فتح مشروع كبير (مثلا، واحدة لك فقط مزامن التحكم بالمصادر النموذج)، وVS تأخذ من الوقت لتحليل كل شيء، وتوليد قاعدة البيانات.

وأنا لا أعرف كيف يتعامل مع التغيرات التدريجية. كما قلت، عندما كنت كتابة التعليمات البرمجية، انها بناء جملة غير صالح 90٪ من الوقت، وسوف reparsing كل شيء كلما المعطله وضع ضريبة ضخمة على وحدة المعالجة المركزية للحصول على فائدة قليلة جدا، خاصة إذا كنت تعديل ملف رأس بايراد عدد كبير من الملفات المصدر.

وأظن أنه إما (أ) reparses فقط كلما كنت في الواقع بناء المشروع (أو ربما عند إغلاق / فتحه)، أو (ب) فإنه نوع من الاعراب المحلي حيث يوزع فقط رمز حول المكان الذي 'هاء فقط تحريرها في بعض الأزياء محدود، فقط للحصول على أسماء الرموز ذات الصلة. منذ C ++ لديها مثل هذه القواعد المعقدة بشكل رائع، فإنه قد تتصرف بشكل غريب في الزوايا المظلمة إذا كنت تستخدم metaprogramming القالب الثقيلة، وما شابه ذلك.

الرابط التالي سيساعدك أكثر..

تسليط الضوء على تركيب:مربع نص ملون سريع لتسليط الضوء على بناء الجملة

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top