اهلا جميعا!
أحاول إنشاء ملحق جديد ولا يبدو أن الوثائق واضحة بشكل رهيب بشأن ما يتطلبه الأمر للانتقال من 0-60.
على سبيل المثال ، 1) كيف تختلف واجهة برمجة التطبيقات للوظائف الإضافية بالضبط عن واجهة برمجة التطبيقات الطرفية الاسمية؟ هل هذا أصلا؟ هل سيحدث ذلك؟ * بالإضافة إلى ذلك ، 2) يقوم بتعديل النموذج الأولي للوحدات الطرفية بالطريقة الأنسب للوظائف الإضافية لتسجيل الوظائف؟ يبدو أنه يسأل فقط عن الاصطدامات. هل هناك أي مساحة أخرى للتسجيل أو مرفق مدينة دبي للإنترنت؟ (أفترض أنه ربما مجرد إضافة الكائن الإضافي ، مثل Terminal.MyAddon.method () لأبسط طريقة ، ولكن بالتأكيد Terminal.addon ('MyAddon'). الطريقة () أكثر صوتًا). 3) أيضًا ، لا يبدو أنه من الواضح كيفية إضافة ملحق تابع لجهة خارجية بالفعل ، حيث تم تشفير جميع الأسماء بشكل ثابت ... (لقد اتخذت لتوسيع Terminal وتوسيع loadAddon ( static loadAddon(String): void;
)) أنا أفترض افتراضات ، لأن المستندات لا تذكر ما إذا كان loadAddon مخصصًا فقط للاستخدام الخاص (غير التابع لجهات خارجية).
لقد قمت بمراجعة بعض الوظائف الإضافية الحالية ، ولكن لا يبدو أنها تحتوي على أكثر التطبيقات اتساقًا.
أقوم بإنشاء اثنين من الإضافات (واحدة على الأقل لرموز الهروب المخصصة) لأنني أرغب في إضافة وظائف منفصلة إلى xTerm.js. المعمارية القيام بذلك بدلاً من مجرد إنشاء طبقة تجريد كبيرة فوق * أعلى xTerm.
بعيدًا إلى حد ما: بالنسبة إلى أكواد الهروب المخصصة ، لا توجد طريقة imho للربط في جهاز الصراف الآلي الافتراضي. لا يزال بإمكانك الحصول على هذه الرموز المخصصة تعمل مع المحلل اللغوي الخاص بك مسبقًا. تسمح مواصفات ANSI بأكواد الهروب المخصصة لأوامر OSC و DCS. ما لم تكن تعرف ما تفعله - التزم بهؤلاء للبقاء متوافقين مع المحلل اللغوي الافتراضي.
مرحبا @ feamsr00 ؛ نقاط جيدة.
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
(وإنشاء تبعية دائرية).
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 النمطية:
سيكون لهذا العديد من الفوائد مثل:
يجب أن يشجعنا نقل هذه إلى مستودعاتهم الخاصة أيضًا على فتح واجهة برمجة التطبيقات بحيث لا تستفيد هذه الوظائف الإضافية الأساسية من واجهة برمجة التطبيقات الخاصة التي يمكن أن تتعطل. ستكون هذه أيضًا فرصة جيدة لإضافة مجموعة اختبار API وهي مجموعة من الاختبارات التي تستخدم واجهة برمجة التطبيقات العامة فقط.
"engines": { "xterm": "..." }
في package.json؟ ربما لا يمكننا إخراج هذا بطريقة لطيفة دون فرض كيفية بناء الأشياء.عند إجراء محادثة مع 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:
فقط بعض الأسئلة التي تخطر ببالي:
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 أكثر قابلية للتوسعة بطريقة منظمة.
بعض الأفكار / الاعتبارات كما قرأت من خلال الاقتراح والمناقشة (بدون ترتيب معين). لا يمثل أي من هذه صفقة ضخمة أو صفقة كبيرة بالنسبة لي ، لكنني أردت الحصول على أفكاري وانطباعاتي هناك:
يبدو أن الحزمة xterm-base
غير ضرورية بالنسبة لي. بأخذ أنظمة مثل grunt / gulp كمثال ، من الشائع بالفعل أن تأخذ المكونات الإضافية peerDependency على الحزمة الرئيسية التي تقوم بتوسيعها. هذا مفيد لأنه يعمل كحارس ضد تحميل الإضافات لإصدار xterm الذي لن يعمل معهم دون تقديم نسخة أخرى من الحزمة في شجرة التبعية. عندما أحتاج إلى الاعتماد على أنواع الحزمة المدرجة باعتبارها تبعية للأقران (أو أرغب في اختبارها) ، سأقوم عمومًا أيضًا بتضمين إصدار متوافق من تلك الحزمة باعتباره devDependency بحيث تكون هناك نسخة متاحة للتطوير. هذا يهتم باستيراد الأنواع عند التطوير ووقت التشغيل دون إدخال حزمة جديدة.
العيبان الرئيسيان لامتلاك حزمة xterm-base
التي يعتمد عليها الجميع هما (1) أنها تفرض سلسلة نشر من خطوتين كلما تغيرت الأنواع و (2) تفقد ضمانات محاذاة الإصدار التي يوفرها peerDependency (منذ ذلك الحين) قد تعتمد الوظيفة الإضافية على xterm-base@4
بينما يتوافق إصدار xterm المستخدم مع xterm-base@5
).
maximumVersion
و minimumVersion
زائدة عن الحاجة باستخدام peerDependency
وتتطلب صيانة إضافية. يجب أن يكون الاعتماد على الأقران وحده كافياً هنا. يبدو أن هذا قد تم الاتفاق عليه بالفعل ولكن لم يتم تحديثه بعد في الاقتراح ، لذلك اعتقدت أنني سأذكره أيضًا.
فيما يتعلق بالمراجع الدائرية بين الملحقات والمحطة ، هل يمثل الاحتفاظ بالمراجع الدائرية مشكلة هنا؟ طالما أن أي أجهزة توقيت / مستمعين / إلخ. تم إغلاقها كجزء من عملية التخلص ، يجب أن يكون جهاز جمع القمامة قادرًا على العثور على المجموعة المعزولة من المراجع الدائرية وتنظيفها. حتى إذا كان المرجع الدائري مصدر قلق ، ألا يكفي إزالة المراجع إلى الوظائف الإضافية على الجانب الطرفي بعد أن يتم استدعاء dispose()
؟ ثم لا يزال هناك مرجع على الجهاز ، لكن الملحق الذي يشير إليه ليس له مراجع بحد ذاته ، لذلك يتم تنظيف السلسلة.
أنا لست جامحًا بشأن إلزام أن تكون الوظائف الإضافية فئات تمت إضافتها / الإشارة إليها بواسطة الفصل نفسه ، ولكن يبدو أنها الطريقة الأفضل / الوحيدة للحصول على Typescript لإرجاع معلومات النوع الصحيح تلقائيًا عند استخدام شيء قائم على السلسلة دون تمرير الإضافات ككائن بدلاً من مصفوفة (والتي بدورها تواجه مشكلة إذا / عندما يكون التسجيل الديناميكي ممكنًا). مع ذلك ، لا أعتقد أن حقل الاسم يوفر أي قيمة مع التصميم الحالي نظرًا لعدم استخدام الأسماء للوصول إلى الملحق وستكون عرضة لتضارب التسمية (خاصةً إذا لم يتم استخدامها كمعرف أساسي لـ الملحق). سأصوت لإزالتها الآن.
مسألة التبعيات بين الوظائف الإضافية وترتيب التحميل مثيرة للاهتمام. تعد مواصفات التبعيات الخاصة بالملحق خادعة لأنها لا تعفي المستخدم من الاضطرار إلى تثبيت الحزمة المعتمدة (يجب أن تكون التبعيات بين الوظائف الإضافية بمثابة تبعيات نظير لتجنب مشاكل الازدواجية).
في هذه المرحلة ، أعتقد أنه من المنطقي جعل المستخدم يحدد جميع الوظائف الإضافية التي يريدون تحميلها والترتيب الذي يريدون تحميله (عبر الترتيب في المصفوفة). إنه يجعل السلوك أسهل للفهم من الخارج ويمنح المستخدمين أقصى قدر من المرونة. كما أنه يجعل الأمور أكثر بساطة عندما يريد الأشخاص القيام بالتفريغ السريع للوظائف الإضافية ، حيث يبدو أن الاقتراح الحالي يتطلب أن تمنع xterm أو تتجاهل بصمت إزالة الملحق الذي لا يزال يحتوي على معالين في قائمة الوظائف الإضافية. من الأسهل على المستخدمين أن يطلقوا النار على أقدامهم ، ولكن من السهل أيضًا إخراج أنفسهم منها. عادةً ما يكون هذا النهج هو الطريقة التي أرى بها الأشياء التي يتم إجراؤها في أنظمة المكونات الإضافية ، على سبيل المثال.
في هذه الحالة ، لا يزال من المحتمل أن تحدد الوظائف الإضافية تبعياتها كجزء من واجهتها (للقدرات المستقبلية المحتملة) ، ولكن مع هذا النهج لا توجد مشكلة في التبعيات الدائرية بين الوظائف الإضافية التي تسبب حلقة لا نهائية أو الوظائف الإضافية التي يتم إسقاطها بصمت لأن xterm لم يعد بحاجة إلى بناء شجرة تبعية من الرسم البياني. إذا كان الملحق غير قادر على العمل بسبب تحميل تبعياته خارج الترتيب ، فيجب أن يفشل بصوت عالٍ على أي حال.
@ princjef بفضل مجموعة من التعليقات ، قيمة للغاية!
ITerminalAddonConstructor<T>
لقد قمت بتجميع فرع يقوم بتنفيذ الاقتراح وتحويل روابط الويب وإرفاقه بالتنسيق الجديد ، يمكنك مشاهدته هنا (يعمل العرض التوضيحي! 😮): 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) أنا أتساءل - كيف يمكنني تمرير التكوين إلى الملحق؟
فكرت في هذا قليلاً لكنني اعتقدت أنه قد يصبح معقدًا للغاية. لقد قمت للتو بتجميع نموذج أولي:
تبدو واجهة برمجة التطبيقات على النحو التالي:
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 على حالات معينة من الجهاز الطرفي (وحتى تغطية الحالة الطرفية خارج الشاشة عندما ندعمها يومًا ما في المستقبل). الأشياء التي تتبادر إلى ذهني (لم أكتب أبدًا ملحقًا بنفسي ، لذا فإن فائدة القائمة مشكوك فيها):
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 لكل مثيل)
هذا شيء كنت أحاول تجنبه ، وهذا يعني أن هناك بعض الوظائف الإضافية التي تعمل مع محطات طرفية متعددة والبعض الآخر لا يعمل. قد يشجع أيضًا على القيام بذلك:
خذ ملحق 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).
التعليق الأكثر فائدة
ابحث أدناه عن اقتراح للوظائف الإضافية المناسبة التي كتبتها أثناء وجودنا في الهواء ✈️ 😄. سيتم تقدير أي ملاحظات بشكل كبير كما لو أن المضي قدمًا وارتكاب أخطاء سيكون من الصعب تغييرها.
/ سم مكعب @ xtermjs / الأساسيةjerchvincentwoochabou @ amejia1jluk
أفضل اقتراح إضافات
الإضافات xterm.js هي مكونات تستخدم واجهة برمجة تطبيقات xterm.js لتوفير وظائف إضافية. إنهم يتبعون هيكلًا معينًا لجعل تطوير الملحق مناسبًا.
يتمثل الاختلاف بين وظيفة الكتابة في الملحق واستخدام واجهة برمجة التطبيقات فقط في أن الوحدة الطرفية تدرك الوظائف الإضافية ويمكن أن توفر روابط / وظائف إضافية لا تحصل عليها عادةً عند البرمجة مقابل واجهة برمجة التطبيقات. من السهل أيضًا تطوير عملك ومشاركته بطريقة متسقة مع المجتمع.
في حين أنه من الممكن بالتأكيد كتابة الوظائف الإضافية في JavaScript ، يتم تشجيع TypeScript بسبب فحوصات نوع المترجم الإضافية ودعم TS من الدرجة الأولى في المكتبة الأساسية.
الإصدار
نظرًا لأن xterm.js هو مشروع حي وتحدث الانكسارات (في الإصدارات الرئيسية) ، فهناك احتمال أن ينكسر الملحق.
تحميل الوظائف الإضافية
بدلاً من ما تم القيام به سابقًا ، تسجيل الوظائف الإضافية في الدالة الثابتة
Terminal.applyAddon
، يتم الآن تمرير الوظائف الإضافية إلىTerminal
في المُنشئ كوسيطة ثانية اختيارية:يتيح ذلك للمحطات الطرفية المختلفة الحصول على مجموعة مختلفة من الوظائف الإضافية ويوفر طريقة ملائمة لتحميل الوظائف الإضافية لكل منها. لاحظ أيضًا أنه يتم توفير
typeof
وITerminalAddon
وحدة xterm-base npm
تحتوي هذه الوحدة على ملفات التصريح التي تحدد واجهة الملحق. السبب الوحيد لوجود هذه الوحدة هو أن الوظائف الإضافية لا تحتاج إلى الاعتماد على الوحدة النمطية
xterm
(وإنشاء تبعية دائرية).يمكن نشر هذا إلى npm بطريقة مماثلة لنشر واجهة برمجة تطبيقات vscode ، ويظل مصدر الحقيقة في مستودع xterm.js (ويشار إليه مباشرة بواسطة xterm.js repo) ، ولكن يتم نشره كوحدة منفصلة للوحدات الإضافية لـ تستهلك.
واجهات
تحدد الإضافات المُنشئ الذي يتم تشغيله أثناء مُنشئ
Terminal
، يجب إضافة الخطافات الأخرى باستخدامTerminal.on
.لاستدعاء الوظائف بالفعل على الملحق ، تحتاج إلى الحصول على مثيل الملحق الخاص بالمحطة. يمكن القيام بذلك عن طريق الاستفادة من
Terminal.getAddon
المعقد نسبيًا والذي يأخذ مُنشئ الملحق ويعيد مثيل الملحق. يجب أن تكون العناصر الداخلية لـgetAddon
سهلة التنفيذ:الوظائف الإضافية الحالية "المجمعة"
ستنتقل كل الوظائف الإضافية المجمعة إلى وحدات إعادة الشراء التالية وقد نشرت وحدات npm النمطية:
سيكون لهذا العديد من الفوائد مثل:
يجب أن يشجعنا نقل هذه إلى مستودعاتهم الخاصة أيضًا على فتح واجهة برمجة التطبيقات بحيث لا تستفيد هذه الوظائف الإضافية الأساسية من واجهة برمجة التطبيقات الخاصة التي يمكن أن تتعطل. ستكون هذه أيضًا فرصة جيدة لإضافة مجموعة اختبار API وهي مجموعة من الاختبارات التي تستخدم واجهة برمجة التطبيقات العامة فقط.
أفكار
"engines": { "xterm": "..." }
في package.json؟ ربما لا يمكننا إخراج هذا بطريقة لطيفة دون فرض كيفية بناء الأشياء.