[−][src]Module xterm_js_sys::xterm
Bindings for the xterm.js public API.
Unfortunately we can't (yet) generate the below from the TypeScript type definitions for xterm.js, so we do it by hand.
This isn't a pure mechanical translation of the xterm.js bindings; docs have been adjusted in places (mainly just to link to the right things on the Rust side) but most importantly interfaces have been converted to either concrete Rust types (that are accessible from JavaScript), imported types (duck types that won't correspond exactly to any concrete type on the JavaScript side and thus can't be constructed from Rust), or imported types + a concrete type that satisfies the interface with a Rust trait with methods that can construct the concrete type for anything satisfying the trait.
Generic interfaces are also problematic; these have been "manually
monomorphized" (i.e. IEvent<Object, Void>
→ FnMut(KeyEventData)
on the
Rust side).
In general, the rule used for interfaces has been:
- If instances are constructed by users of the xterm.js API and written (i.e. given to the xterm.js API and never received through a field access or a method call), we have a corresponding concrete type that satisfies the interface. This cannot be used to manipulate/interact with externally constructed instances of the interface.
- If instances are given by the xterm.js API and never constructed by
users of the API (i.e.
IBuffer
orIBufferLine
), an extern-ed JavaScript type is made (or rather, we getwasm-bindgen
to make the necessary glue so we can access the fields/methods of the interface on whatever object we get passed that has said fields/methods). - If we need to both consume and produce implementations of an interface we'd do both of the above (as of this writing we haven't had to do this for any type).
- If we need to be able to have more than one true concrete type
satisfying the interface on the Rust side, we also create a Rust trait
that matches the shape of the interface along with a blanket impl for
the trait that makes it so that all
wasm-bindgen
'implementations' of the interface also get an impl of the trait. See the section on mirroring interfaces for more details.
Mirroring Interfaces
As mentioned, when it's desirable to construct types that satisfy an
interface within Rust, we create a Rust trait that's mirror of the interface
in question. XtermAddon
(behind the ext
feature) is probably the best
example of this; we want to be able to make it so that addons can be written
in Rust.
So, to make this possible we do these things:
- Make a Rust trait that matches the interface.
- Add a blanket impl so that the Rust trait is implemented for all the
types
wasm-bindgen
produces that impl the type.wasm-bindgen
represents things like extending a class and implementing interfaces withDeref
andAsRef
impls that literally 'convert' the type into an instance of the type they're subclassing/implementing.- This works because internally these instances are represented by a
JsValue
that (I think) is just an object that holds all the methods the object has (including the methods that are part of the interface). When one of these methods is actually called on the Rust side of things, theJsValue
(or a specialwasm-bindgen
reference to it, at least) is passed across the FFI boundary to a special JS function thatwasm-bindgen
made which knows to look up the function that we want in the JS value and call it with the arguments we passed.
Okay! So at this point, we've got a Rust trait that mirrors a JS interface and all things that implement the interface impl the Rust trait automagically. Presumably, when we want to write an impl of the interface on the Rust side of things, we just impl the trait.
And this works, but there's one catch: if we're just using the impls of the interface that we made in Rust, this will work just fine. Implementations that are actually written in JavaScript will internally go call their JS methods and the thing in Rust that's using the trait implementation won't be any the wiser.
But, if we want to pass along implementations written in Rust to a JavaScript user of the interface, this isn't enough.
Addons are a good example, again. It isn't enough to just be able to write something in Rust that has the shape of an addon; the point here is that we're able to pass it to xterm.js and actually use it! So, to do this, there are some more things we have to understand and do.
First some background:
- So,
wasm-bindgen
represents interfaces as concrete types that contain aJsValue
that (presumably) contains all the methods needed to satisfy the interface. - The
AsRef
andDeref
impls pretty much just take the innerJsValue
and put it into a different type that'll use theJsValue
to look up and call different functions; this works because the JS value is just an object with a table of methods — all the methods the object has, not just the ones belonging to the interface we were treating the object as an instance of. The interface types (and regular class types for that matter) and kind of just a window into the object's methods, showing us a limited subset of what the object actually has. - The mechanism by which this casting happens is
JsCast::unchecked_ref
(and the other methods onJsCast
). As the docs on that method say, no checking actually happens! We're pretty much just changing the label that lets us know what methods the corresponding JS value actually has (as in, we're going from, for example,Terminal
toDisposable
but nothing has actually changed; the literal bits that represent the variable are the same, but the type has changed which will let us call different methods that will look up and call different methods on the JS side). There are checked variants inJsCast
; I think the way this works is by having JS functions per type/interface that check that an object actually has all the things it needs to have for an interface.JsCast::instanceof
calls the JS function that does this and the checked casts (i.e.JsCast::dyn_ref
) calls it. - So, anyways, anytime a JS function takes something that "satisfies an
interface" it gets represented, via
wasm-bindgen
, as taking an instance of the type that corresponds to the interface. As in, something that takes an Addon won't takeimpl Addon
or evendyn Addon
, it'll just takeAddon
(sidenote: if you think about what theJsValue
inside the interface types actually contain, it's basically the same as the vtables in trait objects — except that the table has all the methods in the actual type and that this is how all method calls work in JS). - All this is to say that what we need to do is make a
JsValue
that has entries for the methods that are part of the interface where each entry actually points to the Rust functions that are part of the implementation of the trait we're trying to pass along to JS. Once we have such an object, we can cast it as the concrete type thatwasm-bindgen
has given us for the interface and then be on our way.
A couple of other considerations, though:
- First, we'd like to this in a generic way (i.e. make it so that any
Rust trait impl for a particular trait can be turned into it's concrete
interface type counterpart) and we can, but we need to be able to
distinguish between actual JS implementations and Rust implementations
(both of which implement the Rust trait) because we don't want to
'double wrap' the JS implementation (i.e. if we were to do the above
for a JS impl for a particular method call on the interface we'd be
calling a JS function that calls a Rust function that then calls the
actual JS function, when we could have just called the JS function).
- Luckily, this is not hard to remedy; we can have the function that
turns the trait impl into the concrete type be a part of the trait
and we can provide a default impl that does the wrapped. Then, we
can have the blanket impl (which is bounded by
AsRef
anyways) just callas_ref
.
- Luckily, this is not hard to remedy; we can have the function that
turns the trait impl into the concrete type be a part of the trait
and we can provide a default impl that does the wrapped. Then, we
can have the blanket impl (which is bounded by
- Being able to turn Rust function into things that can be called from JS
comes with some restrictions:
- All types in each function have to be Wasm ABI compatible which means no lifetimes or generics or trait objects, etc. This actually isn't a problem for us since we're mirroring a JS interface which means the functions are Wasm ABI compliant anyways.
- The functions and everything they point to have to be
'static
. This is because we can't enforce lifetimes across the FFI boundary. Realistically this probably means usingBox::leak
whenever a Rust trait impl needs to be passed along to JS.- Rather than do this leaking internally, we'll let the user do it.
We enforce the
'static
bit by having theinto_js
method on the trait require a'static
lifetime. So, in order to actually convert their impl for use with JS users will have to leak it. - Update: for symmetry with the actually backed by a JS impl case,
we do preform the leaking (we don't want to require a
'static
reference for JS impls which would require users to leak them unnecessarily).
- Rather than do this leaking internally, we'll let the user do it.
We enforce the
- Traits that take a mutable reference to self and have more than
one method aren't possible (safely) because the closures we pass
along hold a reference to the actual instance and we can't have
mutability with aliasing. So, we'll just make all trait methods only
take an immutable reference to self (as
wasm-bindgen
does). Rust trait implementors will need to use interior mutability.
The final piece required is an extension method that takes the Rust trait
impl instead of the concrete type and then converts it to concrete type
using the trait impl and passes the concrete type instance along to the
wasm-bindgen
method that expects it.
Structs
Buffer | Represents a terminal buffer. |
BufferCell | Represents a single cell in the terminal’s buffer. |
BufferLine | Represents a line in the terminal’s buffer. |
BufferNamespace | Represents the terminal's set of buffers. |
Disposable | An object that can be disposed via a dispose function. |
FunctionIdentifier | Data type to register a |
KeyEventData | Corresponds to |
LinkMatcherOptions | An object containing options for a link matcher. |
LocalizableStrings | The set of localizable strings. |
Marker | Represents a specific line in the terminal that is tracked when scrollback is trimmed and lines are added or removed. This is a single line that may be part of a larger wrapped line. |
Parser | Allows hooking into the parser for custom handling of escape sequences. |
RenderEventData | Corresponds to |
ResizeEventData | Corresponds to |
SelectionPosition | An object representing a selection within the terminal. |
Terminal | The class that represents an xterm.js terminal. |
TerminalAddon | An addon that can provide additional functionality to the terminal. |
TerminalOptions | An object containing start up options for the terminal. |
Theme | Contains colors to theme the terminal with. |
UnicodeHandling | [EXPERIMENTAL] Unicode handling interface. |
UnicodeVersionProvider | [EXPERIMENTAL] Unicode version provider. |
ViewportRange | An object representing a range within the viewport of the terminal. |
ViewportRangePosition | An object representing a cell position within the viewport of the terminal. |
WindowOptions | Enable various window manipulation and report features ( |
Enums
BellStyle | The type of the bell notification the terminal will use. |
BufferType | A string representing the type of a buffer. |
CursorStyle | The style of the cursor. |
FastScrollModifier | The modifier key hold to multiply scroll speed. |
FontWeight | A string representing text font weight. |
LogLevel | A string representing log level. |
RendererType | A string representing a renderer type. |
WideCharacterWidth | Width of a Wide Character. |
Constants
Type Definitions
Color | A Color for use with xterm.js. |
Str | An alias for |