Xterm.js: الوظائف الإضافية المخصصة

تم إنشاؤها على ١٦ نوفمبر ٢٠١٧  ·  39تعليقات  ·  مصدر: xtermjs/xterm.js

اهلا جميعا!

أحاول إنشاء ملحق جديد ولا يبدو أن الوثائق واضحة بشكل رهيب بشأن ما يتطلبه الأمر للانتقال من 0-60.

على سبيل المثال ، 1) كيف تختلف واجهة برمجة التطبيقات للوظائف الإضافية بالضبط عن واجهة برمجة التطبيقات الطرفية الاسمية؟ هل هذا أصلا؟ هل سيحدث ذلك؟ * بالإضافة إلى ذلك ، 2) يقوم بتعديل النموذج الأولي للوحدات الطرفية بالطريقة الأنسب للوظائف الإضافية لتسجيل الوظائف؟ يبدو أنه يسأل فقط عن الاصطدامات. هل هناك أي مساحة أخرى للتسجيل أو مرفق مدينة دبي للإنترنت؟ (أفترض أنه ربما مجرد إضافة الكائن الإضافي ، مثل Terminal.MyAddon.method () لأبسط طريقة ، ولكن بالتأكيد Terminal.addon ('MyAddon'). الطريقة () أكثر صوتًا). 3) أيضًا ، لا يبدو أنه من الواضح كيفية إضافة ملحق تابع لجهة خارجية بالفعل ، حيث تم تشفير جميع الأسماء بشكل ثابت ... (لقد اتخذت لتوسيع Terminal وتوسيع loadAddon ( static loadAddon(String): void; )) أنا أفترض افتراضات ، لأن المستندات لا تذكر ما إذا كان loadAddon مخصصًا فقط للاستخدام الخاص (غير التابع لجهات خارجية).

لقد قمت بمراجعة بعض الوظائف الإضافية الحالية ، ولكن لا يبدو أنها تحتوي على أكثر التطبيقات اتساقًا.

أقوم بإنشاء اثنين من الإضافات (واحدة على الأقل لرموز الهروب المخصصة) لأنني أرغب في إضافة وظائف منفصلة إلى xTerm.js. المعمارية القيام بذلك بدلاً من مجرد إنشاء طبقة تجريد كبيرة فوق * أعلى xTerm.

تفاصيل

  • إصدار tsc.exe: 2.6.1
  • إصدار xterm.js: 3
areapi typenhancement

التعليق الأكثر فائدة

ابحث أدناه عن اقتراح للوظائف الإضافية المناسبة التي كتبتها أثناء وجودنا في الهواء ✈️ 😄. سيتم تقدير أي ملاحظات بشكل كبير كما لو أن المضي قدمًا وارتكاب أخطاء سيكون من الصعب تغييرها.

/ سم مكعب @ xtermjs / الأساسيةjerchvincentwoochabou @ amejia1jluk

أفضل اقتراح إضافات

الإضافات xterm.js هي مكونات تستخدم واجهة برمجة تطبيقات xterm.js لتوفير وظائف إضافية. إنهم يتبعون هيكلًا معينًا لجعل تطوير الملحق مناسبًا.

يتمثل الاختلاف بين وظيفة الكتابة في الملحق واستخدام واجهة برمجة التطبيقات فقط في أن الوحدة الطرفية تدرك الوظائف الإضافية ويمكن أن توفر روابط / وظائف إضافية لا تحصل عليها عادةً عند البرمجة مقابل واجهة برمجة التطبيقات. من السهل أيضًا تطوير عملك ومشاركته بطريقة متسقة مع المجتمع.

في حين أنه من الممكن بالتأكيد كتابة الوظائف الإضافية في JavaScript ، يتم تشجيع TypeScript بسبب فحوصات نوع المترجم الإضافية ودعم TS من الدرجة الأولى في المكتبة الأساسية.

الإصدار

نظرًا لأن xterm.js هو مشروع حي وتحدث الانكسارات (في الإصدارات الرئيسية) ، فهناك احتمال أن ينكسر الملحق.

تحميل الوظائف الإضافية

بدلاً من ما تم القيام به سابقًا ، تسجيل الوظائف الإضافية في الدالة الثابتة Terminal.applyAddon ، يتم الآن تمرير الوظائف الإضافية إلى Terminal في المُنشئ كوسيطة ثانية اختيارية:

interface ITerminalAddonConstructor<T> {
  // Used to make sure the same addon isn't instantiated twice
  readonly NAME: string;
  new(terminal: number): T;
}
class Terminal {
    constructor(
        options: ITerminalOptions,
        addons?: ITerminalAddonConstructor[]
    )
}

يتيح ذلك للمحطات الطرفية المختلفة الحصول على مجموعة مختلفة من الوظائف الإضافية ويوفر طريقة ملائمة لتحميل الوظائف الإضافية لكل منها. لاحظ أيضًا أنه يتم توفير typeof و ITerminalAddon

import { Addon1 } from '...';
import { Addon2 } from '...';

const addons = [Addon1, Addon2];

const terminal = new Terminal({}, addons);

وحدة xterm-base npm

تحتوي هذه الوحدة على ملفات التصريح التي تحدد واجهة الملحق. السبب الوحيد لوجود هذه الوحدة هو أن الوظائف الإضافية لا تحتاج إلى الاعتماد على الوحدة النمطية xterm (وإنشاء تبعية دائرية).

                 xterm.d.ts
                      ^
                      |
             -------------------
             ^        ^        ^
             |        |        |
           xterm  xterm-fit   ...

يمكن نشر هذا إلى npm بطريقة مماثلة لنشر واجهة برمجة تطبيقات vscode ، ويظل مصدر الحقيقة في مستودع xterm.js (ويشار إليه مباشرة بواسطة xterm.js repo) ، ولكن يتم نشره كوحدة منفصلة للوحدات الإضافية لـ تستهلك.

واجهات

تحدد الإضافات المُنشئ الذي يتم تشغيله أثناء مُنشئ Terminal ، يجب إضافة الخطافات الأخرى باستخدام Terminal.on .

interface ITerminalAddon {
    /**
     * The minimum version of xterm.js that this addon is compatible with, in the format
     * `[major, minor, patch]`. if this is higher than the version being used it will throw an
     * `Error` when the addon is loaded.
     */
    minimumVersion: [number, number, number];

    /**
     * The maximum version of xterm.js that this addon is compatible with, in the format
     * `[major, minor, patch]`. If this is defined and lower than the version being used it will
     * throw an `Error` when the addon is loaded.
     * TODO: Should we bother with this? Are people going to bother updating the addon to add this?
     */
    maximumVersion?: [number, number, number];

    // This can be a starting point, with more hooks added later
    constructor(terminal: ITerminal);

    // Terminal will call this when Terminal.dispose is called, this can clean up any
    // references/elements it needs to to shut down gracefully and eventually be
    // used to turn addons off without disposing the terminal
    dispose(): void;

    // We could add more hooks here if we want, or just let addons listen in on internals using
    // `Terminal.on` (which wouldn't be as nicely typed). See xtermjs/xterm.js#808
}

لاستدعاء الوظائف بالفعل على الملحق ، تحتاج إلى الحصول على مثيل الملحق الخاص بالمحطة. يمكن القيام بذلك عن طريق الاستفادة من Terminal.getAddon المعقد نسبيًا والذي يأخذ مُنشئ الملحق ويعيد مثيل الملحق. يجب أن تكون العناصر الداخلية لـ getAddon سهلة التنفيذ:

interface ITerminalAddonConstructor<T> {
  new(terminal: number): T;
}
interface ITerminalAddon {
  a(): void;
}
class SearchAddon implements ITerminalAddon {
  findNext(): void {}
  a(): void {}
}
class FitAddon implements ITerminalAddon {
  fit(): void {}
  a(): void {}
}
class Terminal {
  getAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): T {
    // Search internal addons list for that which matches addonConstructor
    return <T><any>1;
  }
}
const term = new Terminal();
const search = term.getAddon(SearchAddon);
search.findNext(); // Strongly typed simply by using the ctor
const fit = term.getAddon(FitAddon);
fit.fit(); // Strongly typed simply by using the ctor
term.getAddon({}); // error

الوظائف الإضافية الحالية "المجمعة"

ستنتقل كل الوظائف الإضافية المجمعة إلى وحدات إعادة الشراء التالية وقد نشرت وحدات npm النمطية:

  • xtermjs / xterm-addon-attach
  • xtermjs / xterm-الملحق-صالح
  • xtermjs / xterm-addon- ملء الشاشة
  • xtermjs / xterm-addon-search
  • xtermjs / xterm-addon-terminado (توقف لصالح الملحق المجتمعي؟)
  • xtermjs / xterm-addon-web-links
  • xtermjs / xterm-addon-winpty-متوافق
  • xtermjs / xterm-addon-zmodem (توقف لصالح الملحق المجتمعي؟)

سيكون لهذا العديد من الفوائد مثل:

  • تقليل الضوضاء في إعادة الشراء الأساسية من خلال دمج المشكلات
  • تقليل التبعيات / مقدار التعليمات البرمجية في إعادة الشراء الأساسية
  • تبسيط عملية الإنشاء - سيظل العرض التوضيحي يعتمد على البعض ولكن لن يحتاجوا إلى البناء

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

أفكار

  • هل يجب استخدام "engines": { "xterm": "..." } في package.json؟ ربما لا يمكننا إخراج هذا بطريقة لطيفة دون فرض كيفية بناء الأشياء.
  • يمكننا سرد الإضافات المنشورة إلى npm من خلال البحث عن "xterm-addon-" والتوصية باتفاقية التسمية هذه؟
  • واجهة برمجة التطبيقات API التي تمت مناقشتها في # 808 هي في الحقيقة أكثر من واجهة برمجة تطبيقات للتقديم والتي يمكن أن يكون لها المزيد من التفكير فيها بعد # 791

ال 39 كومينتر

بعيدًا إلى حد ما: بالنسبة إلى أكواد الهروب المخصصة ، لا توجد طريقة imho للربط في جهاز الصراف الآلي الافتراضي. لا يزال بإمكانك الحصول على هذه الرموز المخصصة تعمل مع المحلل اللغوي الخاص بك مسبقًا. تسمح مواصفات ANSI بأكواد الهروب المخصصة لأوامر OSC و DCS. ما لم تكن تعرف ما تفعله - التزم بهؤلاء للبقاء متوافقين مع المحلل اللغوي الافتراضي.

مرحبا @ feamsr00 ​​؛ نقاط جيدة.

  1. يجب أن تستخدم الإضافات واجهة برمجة التطبيقات العامة لـ xterm.js فقط لتوفير وظائف إضافية لا تتناسب مع النواة (على سبيل المثال ، إرفاق على مقبس ويب)
  2. لا يعتبر التعديل المباشر للنموذج الأولي ممارسة جيدة ، ولكنه نجح بشكل جيد حتى الآن. يوفر واجهة أبسط من استخدام سجل إضافي. أيضًا لا تتعارض أي من الوظائف الإضافية في المستودع مع أي من الإضافات المتبقية ، لذلك لا يمثل هذا مشكلة حتى الآن.
  3. في الواقع ، لم يتم توثيق كيفية إنشاء وظيفة إضافية ، ولكن يمكننا إصلاح ذلك في 3.0.
  4. ⚠️ بسبب # 1018 ، سنتخلى عن loadAddon لصالح واجهة برمجة تطبيقات أكثر نظافة. آمل أن أفتح علاقات عامة اليوم 😄

jerch شكرا على البصيرة. كنت قلقا بشأن ذلك. هذا النوع يعود إلى سؤال API. حتى بدون واجهة برمجة تطبيقات خاصة للوظائف الإضافية في حد ذاتها ، ليس من الواضح كيف يمكن للمرء فقط استدعاء "Terminal.setParser ()" (خاصة بالنظر إلى عدم وجود مثل هذه الطريقة) لتعيين تلك التبعية. علاوة على ذلك ، سيكون من المفيد وجود بعض عقود الواجهة لـ Parser. هل هناك أي موزعات مخصصة في البرية ، أو أمثلة أخرى حول كيفية المضي قدمًا؟
هل يمكنني اقتراح ميزة على الأقل لإصدار أحداث المحلل اللغوي / ctrl؟

@ feamsr00 حاليًا لا توجد إضافات خارجية حقيقية ، كل شيء مدمج. السبب الرئيسي في عدم تشجيعنا بنشاط على إضافات بناء المجتمع في الوقت الحالي هو أن كل ما يمكنهم فعله هو استدعاء API القياسي الذي لا يمكنك فعله حقًا الكثير من الأشياء المثيرة للاهتمام التي قد تتطلب إضافة.

يمكن للملحق بالطبع الاتصال بالأعضاء الخاصين ولكن من المحتمل أن ينكسر في المستقبل لأننا لا نلتزم بالحفاظ على ثبات واجهات برمجة التطبيقات الخاصة. تتمتع الإضافات المعبأة مع الريبو بفرصة أفضل للاستقرار في أي وقت.

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

100٪ ، أريد حقًا عزل الوظائف الإضافية بطريقة أفضل وأكثر اتساقًا.

في الواقع ، لم يتم توثيق كيفية إنشاء وظيفة إضافية ، ولكن يمكننا إصلاح ذلك في 3.0.

كنت أفضل ألا نتسرع في هذا ولكن بدلاً من ذلك نفكر تمامًا https://github.com/xtermjs/xterm.js/issues/808 ونجعل هذه الطريقة القياسية لبناء ملحق. نحن أيضًا في منتصف الطريق خلال المناقشات حول مستقبل loadAddon ، من الأفضل ترك ذلك يخبز لفترة من الوقت لمعرفة ما إذا كانت هناك أي مطبات في الطريق.

هل هناك أي موزعات مخصصة في البرية ، أو أمثلة أخرى حول كيفية المضي قدمًا؟

تبديل العارضين هو شيء تحدثنا عنه من قبل ، وليس المحللون لأن المحلل اللغوي معقد للغاية. أعتقد أن هناك خططًا للسماح بتوسيع المحلل اللغوي باستخدام https://github.com/xtermjs/xterm.js/issues/576


@ feamsr00 نظرًا لأن لديك بالفعل بعض الأشياء التي تريد https://github.com/xtermjs/xterm.js/issues/808. كان الأمل هنا هو نقل مجموعة من الوظائف المضمنة لاستخدام واجهة برمجة التطبيقات (API) المكونة اللطيفة والمعيارية ثم إتاحتها للاستخدام خارجيًا في الوظائف الإضافية.

دعونا نؤجل هذا حتى نتمكن من إخراج v3

وهكذا ، خرج الإصدار 3 الآن (🎉). ما هي الخطط الخاصة بنظام الملحق؟

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

ابحث أدناه عن اقتراح للوظائف الإضافية المناسبة التي كتبتها أثناء وجودنا في الهواء ✈️ 😄. سيتم تقدير أي ملاحظات بشكل كبير كما لو أن المضي قدمًا وارتكاب أخطاء سيكون من الصعب تغييرها.

/ سم مكعب @ xtermjs / الأساسيةjerchvincentwoochabou @ amejia1jluk

أفضل اقتراح إضافات

الإضافات xterm.js هي مكونات تستخدم واجهة برمجة تطبيقات xterm.js لتوفير وظائف إضافية. إنهم يتبعون هيكلًا معينًا لجعل تطوير الملحق مناسبًا.

يتمثل الاختلاف بين وظيفة الكتابة في الملحق واستخدام واجهة برمجة التطبيقات فقط في أن الوحدة الطرفية تدرك الوظائف الإضافية ويمكن أن توفر روابط / وظائف إضافية لا تحصل عليها عادةً عند البرمجة مقابل واجهة برمجة التطبيقات. من السهل أيضًا تطوير عملك ومشاركته بطريقة متسقة مع المجتمع.

في حين أنه من الممكن بالتأكيد كتابة الوظائف الإضافية في JavaScript ، يتم تشجيع TypeScript بسبب فحوصات نوع المترجم الإضافية ودعم TS من الدرجة الأولى في المكتبة الأساسية.

الإصدار

نظرًا لأن xterm.js هو مشروع حي وتحدث الانكسارات (في الإصدارات الرئيسية) ، فهناك احتمال أن ينكسر الملحق.

تحميل الوظائف الإضافية

بدلاً من ما تم القيام به سابقًا ، تسجيل الوظائف الإضافية في الدالة الثابتة Terminal.applyAddon ، يتم الآن تمرير الوظائف الإضافية إلى Terminal في المُنشئ كوسيطة ثانية اختيارية:

interface ITerminalAddonConstructor<T> {
  // Used to make sure the same addon isn't instantiated twice
  readonly NAME: string;
  new(terminal: number): T;
}
class Terminal {
    constructor(
        options: ITerminalOptions,
        addons?: ITerminalAddonConstructor[]
    )
}

يتيح ذلك للمحطات الطرفية المختلفة الحصول على مجموعة مختلفة من الوظائف الإضافية ويوفر طريقة ملائمة لتحميل الوظائف الإضافية لكل منها. لاحظ أيضًا أنه يتم توفير typeof و ITerminalAddon

import { Addon1 } from '...';
import { Addon2 } from '...';

const addons = [Addon1, Addon2];

const terminal = new Terminal({}, addons);

وحدة xterm-base npm

تحتوي هذه الوحدة على ملفات التصريح التي تحدد واجهة الملحق. السبب الوحيد لوجود هذه الوحدة هو أن الوظائف الإضافية لا تحتاج إلى الاعتماد على الوحدة النمطية xterm (وإنشاء تبعية دائرية).

                 xterm.d.ts
                      ^
                      |
             -------------------
             ^        ^        ^
             |        |        |
           xterm  xterm-fit   ...

يمكن نشر هذا إلى npm بطريقة مماثلة لنشر واجهة برمجة تطبيقات vscode ، ويظل مصدر الحقيقة في مستودع xterm.js (ويشار إليه مباشرة بواسطة xterm.js repo) ، ولكن يتم نشره كوحدة منفصلة للوحدات الإضافية لـ تستهلك.

واجهات

تحدد الإضافات المُنشئ الذي يتم تشغيله أثناء مُنشئ Terminal ، يجب إضافة الخطافات الأخرى باستخدام Terminal.on .

interface ITerminalAddon {
    /**
     * The minimum version of xterm.js that this addon is compatible with, in the format
     * `[major, minor, patch]`. if this is higher than the version being used it will throw an
     * `Error` when the addon is loaded.
     */
    minimumVersion: [number, number, number];

    /**
     * The maximum version of xterm.js that this addon is compatible with, in the format
     * `[major, minor, patch]`. If this is defined and lower than the version being used it will
     * throw an `Error` when the addon is loaded.
     * TODO: Should we bother with this? Are people going to bother updating the addon to add this?
     */
    maximumVersion?: [number, number, number];

    // This can be a starting point, with more hooks added later
    constructor(terminal: ITerminal);

    // Terminal will call this when Terminal.dispose is called, this can clean up any
    // references/elements it needs to to shut down gracefully and eventually be
    // used to turn addons off without disposing the terminal
    dispose(): void;

    // We could add more hooks here if we want, or just let addons listen in on internals using
    // `Terminal.on` (which wouldn't be as nicely typed). See xtermjs/xterm.js#808
}

لاستدعاء الوظائف بالفعل على الملحق ، تحتاج إلى الحصول على مثيل الملحق الخاص بالمحطة. يمكن القيام بذلك عن طريق الاستفادة من Terminal.getAddon المعقد نسبيًا والذي يأخذ مُنشئ الملحق ويعيد مثيل الملحق. يجب أن تكون العناصر الداخلية لـ getAddon سهلة التنفيذ:

interface ITerminalAddonConstructor<T> {
  new(terminal: number): T;
}
interface ITerminalAddon {
  a(): void;
}
class SearchAddon implements ITerminalAddon {
  findNext(): void {}
  a(): void {}
}
class FitAddon implements ITerminalAddon {
  fit(): void {}
  a(): void {}
}
class Terminal {
  getAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): T {
    // Search internal addons list for that which matches addonConstructor
    return <T><any>1;
  }
}
const term = new Terminal();
const search = term.getAddon(SearchAddon);
search.findNext(); // Strongly typed simply by using the ctor
const fit = term.getAddon(FitAddon);
fit.fit(); // Strongly typed simply by using the ctor
term.getAddon({}); // error

الوظائف الإضافية الحالية "المجمعة"

ستنتقل كل الوظائف الإضافية المجمعة إلى وحدات إعادة الشراء التالية وقد نشرت وحدات npm النمطية:

  • xtermjs / xterm-addon-attach
  • xtermjs / xterm-الملحق-صالح
  • xtermjs / xterm-addon- ملء الشاشة
  • xtermjs / xterm-addon-search
  • xtermjs / xterm-addon-terminado (توقف لصالح الملحق المجتمعي؟)
  • xtermjs / xterm-addon-web-links
  • xtermjs / xterm-addon-winpty-متوافق
  • xtermjs / xterm-addon-zmodem (توقف لصالح الملحق المجتمعي؟)

سيكون لهذا العديد من الفوائد مثل:

  • تقليل الضوضاء في إعادة الشراء الأساسية من خلال دمج المشكلات
  • تقليل التبعيات / مقدار التعليمات البرمجية في إعادة الشراء الأساسية
  • تبسيط عملية الإنشاء - سيظل العرض التوضيحي يعتمد على البعض ولكن لن يحتاجوا إلى البناء

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

أفكار

  • هل يجب استخدام "engines": { "xterm": "..." } في package.json؟ ربما لا يمكننا إخراج هذا بطريقة لطيفة دون فرض كيفية بناء الأشياء.
  • يمكننا سرد الإضافات المنشورة إلى npm من خلال البحث عن "xterm-addon-" والتوصية باتفاقية التسمية هذه؟
  • واجهة برمجة التطبيقات API التي تمت مناقشتها في # 808 هي في الحقيقة أكثر من واجهة برمجة تطبيقات للتقديم والتي يمكن أن يكون لها المزيد من التفكير فيها بعد # 791

عند إجراء محادثة مع mofux على Slack ، يمكننا عمل التبعيات بين ITerminalAddon / ITerminalAddonConstructor التي سيتم استخدامها لمنع التنشيط حتى يتم استيفاء الأقسام. يمكن استخدام تبعية الأقران للمساعدة في ضمان تثبيت التبعية.

تعتبر عمليات التحقق من الإصدار min و / أو max زائدة عن الحاجة عند استخدام خيار تبعية الند package.json؟ أفضل السماح لـ npm بمعالجة نطاقات semver ، أي 3.4.0 - 3.5.0 على إضافة { min: [3,4,0], max: [3,5,0] } إلى فئة الوظيفة الإضافية الخاصة بي. يبدو أن القيام بإدارة التبعية يتعارض مع فلسفة يونكس "افعل شيئًا وافعله جيدًا".

كيف يمكنك تعطيل الوظيفة الإضافية؟

تكون عمليات التحقق من الإصدار min و / أو max زائدة عن الحاجة عند استخدام خيار تبعية الند package.json

المزيد من الدردشات مع mofux توصلت إلى نفس النتيجة 😉

كيف يمكنك تعطيل الوظيفة الإضافية؟

لا يمكنك فعل ذلك الآن ، ولكن إذا أردنا دعم ذلك ، فمن المحتمل أن يبدو الأمر كالتالي:

class Terminal {
  disposeAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>);
}

interface ITerminalAddon {
  dispose?(): void;
}

إذا كنت أرغب في تطوير وظيفة إضافية ذات اعتماد اختياري على الوظائف الإضافية الأخرى ، فسيكون من الجيد أن تكون قادرًا على الاتصال بـ getAddon('someOptionalAddon') للحصول على مثيل إضافي. مع وضع ذلك في الاعتبار ، أفضل ما يلي للتسجيل.

class FitAddon implements ITerminalAddon {
  name: 'fit'
  fit(): void
}
xterm.useAddon(FitAddon, function optionalCallback(fit) {
  fit.fit();
});

// And maybe allow an optional name argument
xterm.useAddon('myfit', FitAddon); // Using the name 'fit' would throw here

const fit = xterm.getAddon('myfit');

سيعمل getAddon تم تحميله بشكل زائد أيضًا.

@ pro-src هذه هي الطريقة التي أتخيل بها عمل التبعيات:

// fit.ts
import { ITerminal, ITerminalAddon } from 'xterm-base';
class FitAddon implements ITerminalAddon {
    // Still not 100% sure if we need this
    name: 'fit'
    constructor(private _terminal: ITerminal) {
    }
    fit(): void {
        // Do stuff with this._terminal
    }
}

// myFit.ts
import { ITerminal, ITerminalAddon } from 'xterm-base';
import { FitAddon } from 'xterm-addon-fit';
class MyFitAddon implements ITerminalAddon {
    name: 'myFit'
    // Will peerDependencies just work here and allow access to them?
    dependencies: [ FitAddon ]
    constructor(private _terminal: ITerminal) {
    }
    myFit(): void {
        const fit = this._terminal.getAddon(FitAddon)
    }
}

// app.ts
import { ITerminal, ITerminalAddon } from 'xterm-base';
import { FitAddon } from 'xterm-addon-fit';
import { MyFitAddon } from 'xterm-addon-myfit';
// FitAddon will initialize first, MyFitAddon will follow since it's
// dependencies are met
const term = new Terminal({}, [MyFitAddon, FitAddon]);
term.getAddon(FitAddon).fit();
term.getAddon(MyFitAddon).myFit();

LGTM: +1:

فقط بعض الأسئلة التي تخطر ببالي:

  • الواجهة تشبه إلى حد كبير التركيب. منذ أن أدخلت شركة Typescript بعض ميزات OOP الأنيقة ، فهل يمكننا استخدام الوراثة أيضًا؟ هل سيستمر تسجيل class XYAddon extends ZAddon ... بشكل صحيح؟ إذا كان الأمر كذلك تحت أي اسم؟
  • هل الملحق قابل للتوسعة في مكانه ، وبالتالي تغيير دعنا نقول fitAddon ولا نتخلى عن تبعيات الآخرين؟ لست متأكدًا مما إذا كنا حقًا بحاجة إلى هذا ، فقد رأيت أنظمة مكونات إضافية تسمح "بالحقن" مع الحفاظ على كل شيء آخر سليمًا.

تضمين التغريدة

هذه هي الطريقة التي أتخيل بها عمل التبعيات: ...

رائع ، يبدو أنه يعمل مع الأقسام الاختيارية أيضًا.

const deps = [FitAddon];
Try { deps.push(require('optionalDep')); } catch(e) {}

هل يمتد الفصل XYAddon إلى ZAddon ... مازال يسجل بشكل صحيح؟ إذا كان الأمر كذلك تحت أي اسم؟

نعم ، لا أفهم سبب عدم نجاح ذلك ، كنت أفكر في أن فكرة الخاصية name كانت ضرورية لاستخدامها كمعرّف لذلك سيتم تجاوز الاسم بـ XYAddon .

هل الملحق قابل للتوسيع في مكانه ، وبالتالي تغيير دعنا نقول fitAddon وعدم قطع تبعيات الآخرين؟ لست متأكدًا مما إذا كنا حقًا بحاجة إلى هذا ، فقد رأيت أنظمة مكونات إضافية تسمح "بالحقن" مع الحفاظ على كل شيء آخر سليمًا.

ليس واضحا تماما بالنسبة لي ماذا تقصد هنا؟

من الأفضل حقًا لـ Hyper تحديد الوظائف الإضافية على سبيل المثال: +1:

هل يمكننا تخيل أن يكون لدينا وظيفة addAddon(MyFitAddon) أو أفضل من setAddons([MyFitAddon, FitAddon]) لتغيير الوظائف الإضافية في وقت التشغيل؟ سيكون من الرائع تعيينها مثل الخيارات. نريد حقًا التأكد من أن مكوناتنا الإضافية hyper ساخنة- (غير قابلة للتحميل).

في حالتنا ، عند إضافة / إزالة مكون إضافي ، سنطلب من كل مكون إضافي مصفوفة الوظائف الإضافية الخاصة به xterm ، ودمجها وتمريرها إلى xterm مثيلات موجودة (ومستقبل جديد منها).

اقتراح Tyriar (https://github.com/xtermjs/xterm.js/issues/1128#issuecomment-394142177)

أيضًا ، يعد تأجيل فحوصات التوافق لـ xterm.js أو الوظائف الإضافية الأخرى إلى peerDependencies أمرًا مثاليًا.

أخيرًا ، يجب ترك رأيي حول تعطيل الوظائف الإضافية والتحميل / التفريغ الساخن لمرحلة لاحقة ، حتى نتمكن من التركيز على الأساسيات أولاً.

يعد تحديث نظام الإضافات كما اقترح

تضمين التغريدة
سؤالي الثاني مغطى أيضًا بخاصية الاسم الشيء.

فيما يتعلق بالوظائف الإضافية المترابطة - كيف يتم حل التبعيات؟ هل نحصل على مشكلة MRO مثل MRO أو ركوب الدراجات هنا؟ لا أعتقد أن الناس سيبدأون في إنشاء الكثير من الوظائف الإضافية التابعة لذلك قد تكون هذه مشكلة أكاديمية فقط.

هل يمكننا تخيل أن يكون لدينا وظيفة addAddon (MyFitAddon) أو أفضل setAddons ([MyFitAddon، FitAddon]) لتغيير الوظائف الإضافية في وقت التشغيل؟ سيكون من الرائع تعيينها مثل الخيارات. نريد حقًا التأكد من أن المكونات الإضافية الفائقة لدينا ساخنة (غير قابلة للتحميل).

chabou لم أفكر كثيرًا في إزالة / التخلص من الإضافات في هذه الأمثلة ولكن يمكن إضافتها بعد MVP ، والتي ستؤدي بطبيعة الحال إلى طريقة لتكون قادرًا على الإزالة / الإضافة إلى محطة طرفية موجودة. إذا أردنا أن نكون قادرين على إضافة وظيفة إضافية بعد new Terminal ، فهذا سيضيف بعض التعقيد الذي سنحتاجه للتعامل معه حيث ربما تم إطلاق الحدث open والذي أتخيل أنه سيكون أمرًا بالغ الأهمية للكثيرين الإضافات.

يمكن أن يكون هذا بسيطًا مثل الخصائص المنطقية Terminal.isAttached أو Terminal.state .

فيما يتعلق بالوظائف الإضافية المترابطة - كيف يتم حل التبعيات؟ هل نحصل على مشكلة MRO مثل MRO أو ركوب الدراجات هنا؟ لا أعتقد أن الناس سيبدأون في إنشاء الكثير من الوظائف الإضافية التابعة لذلك قد تكون هذه مشكلة أكاديمية فقط.

jerch كنت أفكر في "وقت التنشيط" افعل هذا:

  Iterate over addons, activating any with dependencies matched
while (an addon was activated)

أعتقد أن هذه الخوارزمية البسيطة يجب أن تغطي جميع القواعد ، ولن يتم تنشيط الإضافات المعتمدة على الدائرية أبدًا ولكن هذا مرغوب فيه 😄

أضفت طريقة dispose إلى الاقتراح أعلاه:

interface ITerminalAddon {
    // Terminal will call this when Terminal.dispose is called, this can clean up any
    // references/elements it needs to to shut down gracefully and eventually be
    // used to turn addons off without disposing the terminal
    dispose(): void;
}

أدركت أننا بحاجة إلى هذا بعد رؤية تعليق معلق على الموارد بعد استدعاء term.dispose() في العرض التوضيحي.

فكرة اخرى:

تثبيط تسريبات ذاكرة التبعية الدائرية

ضع في اعتبارك أنه تم تمرير ITerminalAddonApi إلى الملحق بدلاً من ITerminal :

class FitAddon implements ITerminalAddon {
    name: 'fit'
    constructor(private _api: ITerminalAddonApi) {
    }
    fit(): void {
        this._api.terminal.doStuff();
    }
}

يمكن أن يبدو تنفيذ API الفعلي كما يلي:

interface ITerminalAddonApi {
    readonly terminal: ITerminal
}

class TerminalAddonApi implements ITerminalAddonApi {
    constructor(private _terminal: ITerminal) {
    }
    public get terminal(): ITerminal {
        return this._terminal;
    }
    public dispose(): void {
        this._terminal = null;
    }
}

سيؤدي هذا إلى تثبيط التمسك الإضافي بالمراجع ITerminal ، مما يسمح بتحريرها عند التخلص من الجهاز (حيث سيؤدي Terminal.dispose إلى تشغيل TerminalAddonApi.dispose ).

أعدت تسمية https://github.com/xtermjs/xterm-addon-ligatures repo لتتماشى مع نظام التسمية المذكور هنا

آسف للتأخر قليلاً في اللعبة: ابتسم: اقتراح Tyriar بشكل عام هو خطوة رائعة نحو جعل xterm.js أكثر قابلية للتوسعة بطريقة منظمة.

بعض الأفكار / الاعتبارات كما قرأت من خلال الاقتراح والمناقشة (بدون ترتيب معين). لا يمثل أي من هذه صفقة ضخمة أو صفقة كبيرة بالنسبة لي ، لكنني أردت الحصول على أفكاري وانطباعاتي هناك:

  1. يبدو أن الحزمة xterm-base غير ضرورية بالنسبة لي. بأخذ أنظمة مثل grunt / gulp كمثال ، من الشائع بالفعل أن تأخذ المكونات الإضافية peerDependency على الحزمة الرئيسية التي تقوم بتوسيعها. هذا مفيد لأنه يعمل كحارس ضد تحميل الإضافات لإصدار xterm الذي لن يعمل معهم دون تقديم نسخة أخرى من الحزمة في شجرة التبعية. عندما أحتاج إلى الاعتماد على أنواع الحزمة المدرجة باعتبارها تبعية للأقران (أو أرغب في اختبارها) ، سأقوم عمومًا أيضًا بتضمين إصدار متوافق من تلك الحزمة باعتباره devDependency بحيث تكون هناك نسخة متاحة للتطوير. هذا يهتم باستيراد الأنواع عند التطوير ووقت التشغيل دون إدخال حزمة جديدة.

    العيبان الرئيسيان لامتلاك حزمة xterm-base التي يعتمد عليها الجميع هما (1) أنها تفرض سلسلة نشر من خطوتين كلما تغيرت الأنواع و (2) تفقد ضمانات محاذاة الإصدار التي يوفرها peerDependency (منذ ذلك الحين) قد تعتمد الوظيفة الإضافية على xterm-base@4 بينما يتوافق إصدار xterm المستخدم مع xterm-base@5 ).

  2. maximumVersion و minimumVersion زائدة عن الحاجة باستخدام peerDependency وتتطلب صيانة إضافية. يجب أن يكون الاعتماد على الأقران وحده كافياً هنا. يبدو أن هذا قد تم الاتفاق عليه بالفعل ولكن لم يتم تحديثه بعد في الاقتراح ، لذلك اعتقدت أنني سأذكره أيضًا.

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

  4. أنا لست جامحًا بشأن إلزام أن تكون الوظائف الإضافية فئات تمت إضافتها / الإشارة إليها بواسطة الفصل نفسه ، ولكن يبدو أنها الطريقة الأفضل / الوحيدة للحصول على Typescript لإرجاع معلومات النوع الصحيح تلقائيًا عند استخدام شيء قائم على السلسلة دون تمرير الإضافات ككائن بدلاً من مصفوفة (والتي بدورها تواجه مشكلة إذا / عندما يكون التسجيل الديناميكي ممكنًا). مع ذلك ، لا أعتقد أن حقل الاسم يوفر أي قيمة مع التصميم الحالي نظرًا لعدم استخدام الأسماء للوصول إلى الملحق وستكون عرضة لتضارب التسمية (خاصةً إذا لم يتم استخدامها كمعرف أساسي لـ الملحق). سأصوت لإزالتها الآن.

  5. مسألة التبعيات بين الوظائف الإضافية وترتيب التحميل مثيرة للاهتمام. تعد مواصفات التبعيات الخاصة بالملحق خادعة لأنها لا تعفي المستخدم من الاضطرار إلى تثبيت الحزمة المعتمدة (يجب أن تكون التبعيات بين الوظائف الإضافية بمثابة تبعيات نظير لتجنب مشاكل الازدواجية).

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

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

@ princjef بفضل مجموعة من التعليقات ، قيمة للغاية!

  1. نقاط جيدة ، تبدو مثل الاتجاه الذي يجب أن نسير فيه
  2. : +1:
  3. نقطة جيدة ، أعتقد أن هذا هو أنني أشعر بجنون العظمة بشكل مفرط بشأن المراجع الدائرية بعد التعامل مع https://github.com/xtermjs/xterm.js/pull/1525 ، إذا كانت لا تزال مشكلة ، فيمكننا فعل ذلك لاحقًا.
  4. لا أعتقد أننا بحاجة إلى اسم إذا استخدمنا طريقة ITerminalAddonConstructor<T>
  5. قد يكون الفشل بصوت عالٍ (console. warn / error / throw) عند التسجيل / التخلص من الوظائف الإضافية فكرة جيدة. يجب عليك دائمًا القيام بذلك بالترتيب الصحيح وإلا فقد يؤدي ذلك إلى حدوث أخطاء

لقد قمت بتجميع فرع يقوم بتنفيذ الاقتراح وتحويل روابط الويب وإرفاقه بالتنسيق الجديد ، يمكنك مشاهدته هنا (يعمل العرض التوضيحي! 😮): https://github.com/xtermjs/xterm.js/compare / السيد .. الإطار : 1128_addons؟ توسيع = 1

فيما يلي الوحدات:

ها هي API:

  class Terminal {
    loadAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): T;
    disposeAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): void;
    getAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): T;
  }

  export interface ITerminalAddonConstructor<T extends ITerminalAddon> {
    new(terminal: Terminal): T;
  }

  export interface ITerminalAddon {
    /**
     * This function includes anything that needs to happen to clean up when
     * the addon is being disposed.
     */
    dispose(): void;
  }

قد تلاحظ واجهة برمجة تطبيقات Terminal.disposeAddon ، أعتقد أن أحد أفضل الأشياء في القيام بذلك هو إدارة دورة حياة الوظائف الإضافية بحيث يمكن تبديلها وإخراجها دون إعادة إنشاء Terminal . كانت إحدى الأفكار التي خطرت لي هي نقل عارض WebGL إلى ملحق ، في المقام الأول كوسيلة لاختباره بطريقة منخفضة المخاطر حقًا (الاشتراك في واجهات برمجة التطبيقات الخاصة برقعة القرود) ، ومن المحتمل الاحتفاظ بها بهذا الشكل نظرًا لوجود فوائد كبيرة في عدم تحميل عارض WebGL إذا كنت لا تستخدمه.

اسمحوا لي أن أعرف ما هو رأيك 😃

أنا أحب API بشكل عام 👍

(1) أنا أتساءل - كيف يمكنني تمرير التكوين إلى الملحق؟ بقدر ما أستطيع أن أرى ، فإن الطريقة الوحيدة هي استخدام استدعاء منفصل لمثيل الملحق لبعض الطرق المخصصة مثل init :

const myAddon = term.loadAddon(MyAddon);
myAddon.init({ /*...config*/ })

يحتاج تمرير IMO لتكوين إلى مكون إضافي إلى قصة أفضل من هذا. ربما تتم إضافة تهيئة اختيارية كمعامل ثاني إلى طريقة term.loadAddon ، والتي تمررها بعد ذلك إلى مُنشئ الملحق؟

// addon definition
class MyAddon implements ITerminalAddon {
  constructor(terminal, config) {
  }
  dispose() {
  }
}

// register addon
const myAddon = term.loadAddon(MyAddon, { /*...config*/ });

والتي يمكن أن تبدو كالتالي بالنسبة للملحق الإضافي:

term.loadAddon(AttachAddon, { socket: mySocket });

وانت انتهيت.

(2) هل من المنطقي إضافة رابط دورة الحياة إلى وقت فتح الجهاز ( term.open )؟ أعتقد أن الوظائف الإضافية التي تحتاج إلى الوصول إلى أبعاد الشاشة أو DOM ستستفيد من هذا الخطاف.

تضمين التغريدة

(2) هل من المنطقي إضافة خطاف دورة الحياة إلى وقت فتح الجهاز (مصطلح مفتوح)؟ أعتقد أن الوظائف الإضافية التي تحتاج إلى الوصول إلى أبعاد الشاشة أو DOM ستستفيد من هذا الخطاف.

فكرة رائعة ، onOpen ، onDomAttach ؟ (بعد التغيير من https://github.com/xtermjs/xterm.js/issues/1505)

(1) أنا أتساءل - كيف يمكنني تمرير التكوين إلى الملحق؟

فكرت في هذا قليلاً لكنني اعتقدت أنه قد يصبح معقدًا للغاية. لقد قمت للتو بتجميع نموذج أولي:

image

تبدو واجهة برمجة التطبيقات على النحو التالي:

  export class Terminal {
    loadAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): T;
    loadAddonWithConfig<T extends ITerminalAddonWithConfig<K>, K>(addonConstructor: ITerminalAddonWithConfigConstructor<T, K>, config: K): T;
  }

  export interface ITerminalAddonConstructor<T extends ITerminalAddon> {
    new(terminal: Terminal): T;
  }

  export interface ITerminalAddonWithConfigConstructor<T extends ITerminalAddonWithConfig<K>, K> {
    new(terminal: Terminal, config: K): T;
  }

  export interface ITerminalAddon {
    dispose(): void;
  }

  // The types must be duplicated, extending ITerminalAddon means this could
  // be passed into Terminal.loadAddon
  export interface ITerminalAddonWithConfig<K> {
    dispose(): void;
  }

ضمني الملحق:

export interface IWebLinksAddonConfig {
  handler?: (event: MouseEvent, uri: string) => void;
  options?: ILinkMatcherOptions;
}

export class WebLinksAddonWithConfig implements ITerminalAddonWithConfig<IWebLinksAddonConfig> {
  // config can be omitted here without warning
  constructor(private _terminal: Terminal, config: IWebLinksAddonConfig) {
  }

  ...
}

الفرق: https://github.com/Tyriar/xterm.js/commit/9822953d012ab167a0ad17a557595aea124ac97a

أفكار؟

Yupp حقا API لطيفة حتى الآن. : +1:

تعجبني فكرة خطافات دورة الحياة ، وبهذه الطريقة يمكن أن يعمل ppl على حالات معينة من الجهاز الطرفي (وحتى تغطية الحالة الطرفية خارج الشاشة عندما ندعمها يومًا ما في المستقبل). الأشياء التي تتبادر إلى ذهني (لم أكتب أبدًا ملحقًا بنفسي ، لذا فإن فائدة القائمة مشكوك فيها):

  • afterInit
    كندة تشرح نفسها ، كلما ولدت مثيل طرفي. يمكن للملحق الخالص خارج الشاشة أن يتجاهل خطافات DOM.
  • afterDOMattach / didMount (أسلوب رد الفعل؟)
    إذا كانت الوظائف الإضافية بحاجة إلى إعداد DOM ، فيجب أن يكون DOM متاحًا بالفعل.
  • قبل DOMdetach / willUnmount
    لست متأكدًا من هذا ، هل من الممكن ربط سلسلة التخلص من الحبيبات الدقيقة؟ قد لا يزال مفيدًا للملحق ، إذا كان عليه تنظيف الحالة بينما لا يزال من الممكن الوصول إلى DOM.
  • قبل التخلص منه
    حسنًا - هل يحتاج الملحق إلى غرفة تنظيف قبل التخلص من شجرة الكائن بالكامل؟ ربما يكون dispose قد تأخر بالفعل ...

afterInit

حسنًا ، هذا هو المُنشئ بشكل أساسي ، وكانت الخطة الأولية هي إعطاء مجموعة من الوظائف الإضافية لـ Terminal.ctor ولكن يبدو أن ذلك يؤدي إلى تعقيد الأشياء ، خاصة أنه لن يتم عرض أي شيء حتى يتم استدعاء الفتح على أي حال.

حسنًا - هل يحتاج الملحق إلى غرفة تنظيف قبل التخلص من شجرة الكائن بالكامل؟ قد يكون التخلص متأخرا بالفعل ...

يمكنك ان تعطي مثالا؟

كنت أفكر أيضًا أننا ربما لا نحتاج حتى إلى Terminal.disposeAddon و Terminal.getAddon . إذا كنت ترغب في إدارة دورة حياة الملحق بنفسك (التعطيل في وقت التشغيل) أو كنت بحاجة إلى التفاعل معها لاحقًا (على سبيل المثال ، البحث) ، فما عليك سوى الاحتفاظ بمرجع من loadAddon ، فإن التفاف وظيفة التخلص عند التحميل يجعل التخلص من البرنامج imo زائداً عن الحاجة.

يمكنك ان تعطي مثالا؟

حسنًا ، ليس لدي أي فكرة حاليًا عن ترتيب التخلص ، لذلك هذا نظري تمامًا. ماذا لو اعتمد الملحق على حالة مكون أخرى (شيء داخلي أو ملحق آخر) ، وكان عليه تنظيف الأشياء ، لكن المكون الآخر قد اختفى بالفعل؟ المثال الذي يتبادر إلى ذهني هو ملحق يقوم بترتيب الحالة الطرفية الحالية لتتمكن من استئنافها لاحقًا.
باستخدام beforeDispose (يمكن تشغيله كمهمة أولى terminal.dispose ) يمكن لأي ملحق أن يستعد بأمان للتخلص القادم بينما لا تزال البيئة سليمة.

المثال الذي يتبادر إلى ذهني هو ملحق يقوم بترتيب الحالة الطرفية الحالية لتتمكن من استئنافها لاحقًا.

حسنًا ، دعنا نضيف إذا طلب شخص ما ، فقد تكون هناك حاجة إذا أضفنا تبعيات إضافية

Tyriar آسف ، لقد نسيت خيارات التكوين التي تمت كتابتها في رسالتي السابقة. ألا يمكننا السماح للمستخدم بإنشاء مثيل للوظيفة الإضافية ، ثم السماح له بتمرير مثيل الوظيفة الإضافية إلى term.loadAddon ؟

// addon definition
class MyAddon implements ITerminalAddon {

  // addon constructor
  constructor(config: IMyAddonConfig) {
  }

  // called when initiating the plugin
  onLoad(terminal: Terminal) {
  }

  // called when term.open() was called
  onOpen(terminal: Terminal) {
  }

  // called on term.dispose()
  dispose(terminal: Terminal) {
  }

}

// create addon instance (note: the consumer instanciates the addon, not us)
const myAddon = new MyAddon({ /*config*/ });
term.loadAddon(myAddon);

لذا فإن onLoad سيكون في الأساس الخطاف الذي سيتعامل مع ما تم القيام به سابقًا في constructor . سيكون لهذا أيضًا ميزة أنه يمكن للمرء إنشاء ملحق بدون فصل دراسي في وقت التشغيل (لست متأكدًا مما إذا كان مفيدًا أم لا):

term.loadAddon({
  onLoad(terminal) {
  },
  dispose(terminal) {
  }
});

تحديث
ميزة أخرى مع هذا النهج هي أنه يمكن للمرء استخدام نفس مثيل الملحق لمحطات متعددة. أحد الأمثلة على ذلك هو الإضافات التي تحتفظ بذاكرة تخزين مؤقت داخلية ، والتي يمكن إعادة استخدامها بسهولة مع هذا الأسلوب ( constructor يُدعى مرة واحدة ، onLoad يُستدعى لكل مثيل)

ماذا تعتقد؟

ألا يمكننا السماح للمستخدم بإنشاء مثيل للوظيفة الإضافية ، ثم السماح له بتمرير مثيل الملحق إلى term.loadAddon؟

أفترض أنه يمكننا التخلص من جميع عناصر المنشئ إذا كان لدينا واجهة برمجة تطبيقات واحدة فقط Terminal.loadAddon . بالتأكيد يبدو أبسط.

ميزة أخرى مع هذا النهج هي أنه يمكن للمرء استخدام نفس مثيل الملحق لمحطات متعددة. أحد الأمثلة على ذلك هو الإضافات التي تحتفظ بذاكرة تخزين مؤقت داخلية ، والتي يمكن إعادة استخدامها بسهولة مع هذا الأسلوب (يُطلق على المُنشئ مرة واحدة ، ويستدعي onLoad لكل مثيل)

هذا شيء كنت أحاول تجنبه ، وهذا يعني أن هناك بعض الوظائف الإضافية التي تعمل مع محطات طرفية متعددة والبعض الآخر لا يعمل. قد يشجع أيضًا على القيام بذلك:

https://github.com/xtermjs/xterm.js/blob/509ce5fa3a698ee7847419117e9dd6b979b105bf/src/addons/attach/attach.ts#L23

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

ماذا لو فعلنا شيئًا كهذا لمنعه؟

loadAddon(addon: ITerminalAddon): void {
  if (addon.__isLoaded) {
    throw ...
  }
  ...
  addon.__isLoaded = true;
}

فكرة للوحدات الإضافية المجمعة: https://github.com/xtermjs/xterm.js/pull/1714#issuecomment -454898319

النموذج الجديد أبسط بكثير:

class Terminal {
    /**
     * Loads an addon into this instance of xterm.js.
     * <strong i="6">@param</strong> addon The addon to load.
     */
    loadAddon(addon: ITerminalAddon): void;
}

export interface ITerminalAddon {
    /**
     * This is called when the addon is activated within xterm.js.
     */
    activate(terminal: Terminal): void;

    /**
     * This function includes anything that needs to happen to clean up when
     * the addon is being disposed.
     */
    dispose(): void;
}

يتيح هذا للمُضَمِّن إنشاء الملحق كما يحلو له ، ونحن نتخلص من تعقيد نظام ctor والطرق الإضافية. إذا أراد شخص ما الاحتفاظ بمرجع حوله ، فما عليك سوى التمسك بالملحق بعد التسجيل:

term.loadAddon(new WebLinksAddon());
const attachAddon = new AttachAddon();
term.loadAddon(attachAddon);

// ...

attachAddon.attach(...);

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

const addon = {
  activate(term: Terminal): void {
    if (term.element) {
      // it's open
    } else {
      // handle open event
      term.onOpen(() => ...);
    }
  }
}

تجدر الإشارة إلى أنني لم أكن أخطط لتصدير الوظائف الإضافية على window (راجع https://github.com/xtermjs/xterm.js/issues/2015).

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

jerch picture jerch  ·  3تعليقات

7PH picture 7PH  ·  4تعليقات

fabiospampinato picture fabiospampinato  ·  4تعليقات

Tyriar picture Tyriar  ·  4تعليقات

johnpoth picture johnpoth  ·  3تعليقات