Terminal Content

All terminal content is TAST. You can add new TAST, change existing, or get what it is present.

Introduction

It is recommendable to understand how the Content is represented and manipulated.

Visible TAST or Raw TAST

Terminal Raw TAST is the TAST that set-actions create (like appendTerminalTAST). It might match the visible TAST, but it is not necessary.

Terminal Visible TAST corresponds to what the terminal is showing. You have to consider the following things:

  1. Not everything is visible: some inlines are hidden, they are invisible. Those inlines are not part of the visible TAST.

  2. There are decorators: decorators make visual enhancements over the content of the terminal. These enhancements do not modify what the terminal contains but the representation. The visible TAST contains those transformations.

  3. There are more things than inlines: in addition to decorators that may add Action and Style elements to the TAST, there are also Root elements. For each line of the Visible TAST, there is a Root element that encapsulates its contents.

Use Raw TAST when you need to know what there is in the terminal. For example, it is handy to show hidden inlines. Raw TAST is often related to operations over terminal TAST content. Use Visible TAST when you need to know what the user is seeing.

TAST vs Text

All terminal content is TAST. Although tastHelper.convertToText converts any TAST into Text, the terminal provides useful methods to obtain or manipulate text directly. These methods are optimized to be faster than get the TAST and transform it. Use these methods instead of TAST ones if you deal only with text.

Overwrite mode, Insert mode, and splice

The terminal has two possible modes: overwrite and insert. All terminals share the same mode, terminal mode operations can change it at any moment.

When the terminal is in overwrite mode, writing TAST or writing chars through the keyboard makes that each new character replaces the old one. It keeps everything in the same position. If the written text exceeds the terminal width, it continues overwriting the next line and its contents. It does not put new lines between the existing lines. When the user deletes content from the terminal in overwrite mode, it replaces the content by white spaces. If the user reaches the first column of a line and tries to keep removing things, it just wraps to the next line. It does not remove lines from the terminal.

When the terminal is in insert mode, writing TAST or writing chars through the keyboard makes each new character displace existing characters towards the end of the line. While inserting new characters, it is possible that new characters plus existing characters may not fit in the terminal width. Unlike a text editor, the terminal in insert mode does not move existing exceeding characters towards the next line, it discard and removes those characters. If the written text exceeds the terminal width, then it continues inserting new text into the next existing line and its contents. It does not put new lines between the existing lines. When the user deletes content from the terminal, it removes the corresponding characters and displaces remaining characters towards the first column. It fill lasts columns with virtual spaces. Unlike text editors, it does not remove lines and does not move characters from one line to the previous one. When it reaches the first column of a line, it leaves all the remaining content in the line untouched and moves to the end of the following line.

Splice is a whole complete different operation. It is an operation designed to manipulate the TAST of the terminal directly and inserts TAST as exactly specified. Overwrite and insert mode behaviors are not considered. It allows us to move content from one line to another, remove lines, insert lines, and almost any operation without restrictions.

Type anywhere

The terminal supports type anywhere. The dimension of the terminal is finite; there is a maximum column and a current height. The user can write at any of those positions at any time. Current height changes with the user content, new content may increase the terminal height, and removing content may shrink the terminal height.

It is also possible for the user to start writing into an unexisting column, or into an existing line. If the user writes into an unexisting column, the terminal automatically adds new spaces just before the current column until this column exists. If the user writes into an unexisting line, the terminal automatically adds new lines until it reaches the position of the user. The movement does not create these new lines or column; only changes in content do.

Ranges

Many terminal operations, requires, accepts or returns a range. A range is a delimited part of the terminal specified by a from position and a to position. Both positions have a line and a column. The from position must always precede or equal the to position in the same range. If this condition is not satisfied, the result is undetermined. The from range position is part of the range, but it excludes the to position. Any range with the same from and to positions is an empty range.

Host integration

The terminal is integrated with host. There are some actions affected by the current host configuration. Some of these actions are getTerminalPreviousSOM and getTerminalCryptic.

getTerminalVisibleTAST

It is a selector that given the state and a terminalId it retrieves the TAST that is currently visible to the user. This TAST contains all changes made by decorator but does not include hidden inlines.

Usage
Copy
getTerminalVisibleTAST((state: State), {
  terminalId: String,
  from: { line: Number, column: Number },
  to: { line: Number, column: Number },
});
  • state State

    Required. It is the global state of the application.

  • terminalId String

    Required. It selects from which terminal retrieves the content.

  • from Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: 1, column: 0 }.

  • to Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: lastLine + 1, column: 0 }.

Example
Copy
const getTerminalVisibleTAST = selectorsHelper.make('getTerminalVisibleTAST');
const { getState } = storeHelper;

createTerminalWithTAST(['ABC\n123']);
const result = getTerminalVisibleTAST(getState(), { terminalId });
expect(result).toEqual([
  {
    type: 'Root',
    children: ['ABC'],
  },
  {
    type: 'Root',
    children: ['123'],
  },
]): TAST;

getTerminalVisibleText

It is a selector that given the state and a terminalId it retrieves the text that is currently visible to the user. This text contains all changes made by decorator but does not include the text inside hidden inlines.

Usage
Copy
getTerminalVisibleText(state: State, {
  terminalId: String,
  from: { line: Number, column: Number },
  to: { line: Number, column: Number },
}): String;
  • state State

    Required. It is the global state of the application.

  • terminalId String

    Required. It selects from which terminal retrieves the content.

  • from Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: 1, column: 0 }.

  • to Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: lastLine + 1, column: 0 }.

Example
Copy
const getTerminalVisibleText = selectorsHelper.make('getTerminalVisibleText');
const { getState } = storeHelper;

createTerminalWithTAST(['ABC\n123']);
const result = getTerminalVisibleText(getState(), { terminalId });
expect(result).toEqual('ABC\n123');

getTerminalRawTAST

It is a selector that given the state and a terminalId it retrieves the corresponding TAST. This TAST contains all inlines, including the hidden one, but contains none of the changes made by decorators. It also splits TAST entities and text by lines.

Usage
Copy
getTerminalRawTAST(state: State, {
  terminalId: String,
  from: { line: Number, column: Number },
  to: { line: Number, column: Number },
}): TAST;
  • state State

    Required. It is the global state of the application.

  • terminalId String

    Required. It selects from which terminal retrieves the content.

  • from Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: 1, column: 0 }.

  • to Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: lastLine + 1, column: 0 }.

Example
Copy
const getTerminalRawTAST = selectorsHelper.make('getTerminalRawTAST');
const { getState } = storeHelper;

createTerminalWithTAST(['ABC\n123']);

const result = getTerminalRawTAST(getState(), { terminalId });
expect(result).toEqual(['\nABC', '\n123']);

getTerminalRawText

 

It is a selector that given the state and a terminalId it retrieves the TAST that is currently Raw to the user. This text does not containes changes made by decorators, but also do not contain text from hidden inlines.

Usage
Copy
getTerminalRawText(state: State, {
  terminalId: String,
  from: { line: Number, column: Number },
  to: { line: Number, column: Number },
}): String;
  • state State

    Required. It is the global state of the application.

  • terminalId String

    Required. It selects from which terminal retrieves the content.

  • from Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: 1, column: 0 }.

  • to Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: lastLine + 1, column: 0 }.

Copy
const getTerminalRawText = selectorsHelper.make('getTerminalRawText');
const { getState } = storeHelper;

createTerminalWithTAST(['ABC\n123']);

const result = getTerminalRawText(getState(), { terminalId });
expect(result).toEqual('\nABC\n123');

findTerminalVisibleRange

A selector that finds a TAST range inside the terminal visible TAST. It can look for a specific text or TAST. It also can start searching at specific position.

Usage
Copy
findTerminalVisibleRange(state: State, {
  terminalId: String,
  matchText: String | Function,
  matchEntity: Function
  fromPosition: { line: Number, column: Number },
}): null | {
  value: TAST,
  from: { line, column },
  to: { line, column }
};
  • state State

    Required. It is the global state of the application.

  • terminalId String

    Required. It selects from which terminal retrieves the content.

  • matchText String|RegExp|Function

    Optional. If specified, looks for the given text, a regular expression, or executes a function that receives a text and returns a possible match.

  • matchEntity Function

    Optional.If specified, executes a function over each TAST entity (string, object or array of TAST) that returns a possible match.

  • fromPosition Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: 1, column: 0 }. Use the same value received at to result as next call fromPosition to find all matches.

If looks for a string inside the terminal if matchText is a text, looks for a test matching a regular expression if matchText is a regular expression, or a custom text matcher function can be provided at matchText. In this case, the matchText function receives a text present in the terminal, and this matching function has to decide if it matches and what matches, it has to return a value and an index. The matchText returns null if nothing matches in the current text.

It also can look for any arbitrary TAST, not just text. In that case matchEntity should be provided. It calls the matchEntity function many times, one for each piece of TAST. The matchEntity receives a piece of that TAST (that can be a string, an object, or an array), and it must return true if it has a match, or false if it is not.

Note that either matchText or matchEntity should be present. Both, matchText and matchEntity can be specified at the same time, if it is the case it returns the first that matches.

The matchText and matchEntity functions should have no side effects. They can be called any arbitrary number of times while the algorithm is trying to explore the TAST.

findTerminalLastVisibleRange

This is the selector for reverese find of visible TAST. It has the same signature that has findTerminalVisibleRange but if finds TAST backwards.

Finds a TAST range inside the terminal visible TAST. It can look for a specific text or TAST. It also can start searching at specific position.

Usage
Copy
getTerminalRawText(state: State, {
  terminalId: String,
  matchText: String | Function,
  matchEntity: Function
  fromPosition: { line: Number, column: Number },
}): null | {
  value: TAST,
  from: { line, column },
  to: { line, column }
};
  • state State

    Required. It is the global state of the application.

  • terminalId String

    Required. It selects from which terminal retrieves the content.

  • matchText String|RegExp|Function

    Optional. If specified, looks for the given text, a regular expression, or executes a function that receives a text and returns a possible match.

  • matchEntity Function

    Optional.If specified, executes a function over each TAST entity (string, object or array of TAST) that returns a possible match.

  • fromPosition Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: 1, column: 0 }. Use the same value received at to result as next call fromPosition to find all matches.

If looks for a string inside the terminal if matchText is a text, looks for a test matching a regular expression if matchText is a regular expression, or a custom text matcher function can be provided at matchText. In this case, the matchText function receives a text present in the terminal, and this matching function has to decide if it matches and what matches, it has to return a value and an index. The matchText returns null if nothing matches in the current text.

It also can look for any arbitrary TAST, not just text. In that case matchEntity should be provided. It calls the matchEntity function many times, one for each piece of TAST. The matchEntity receives a piece of that TAST (that can be a string, an object, or an array), and it must return true if it has a match, or false if it is not.

If both, matchText and matchEntity are specified at the same time, it returns the last matching.

The matchText and matchEntity functions should have no side effects. They can be called any arbitrary number of times while the algorithm is trying to explore the TAST backwards.

findTerminalRawRange

A selector that finds a TAST range inside the terminal Raw TAST. It can optionally searching at specific position. It does not support find by text or by RegExp.

Usage
Copy
findTerminalRawRange(state: State, {
  terminalId: String,
  match: Function
  fromPosition: { line: Number, column: Number },
}): null | {
  value: TAST,
  isVisible: Boolean,
  from: { line: Number, column: Number },
  to: { line: Number, column: Number }
};
  • state State

    Required. It is the global state of the application.

  • terminalId String

    Required. It selects from which terminal retrieves the content.

  • match String|RegExp|Function

    Optional. If specified, looks for the given text, a regular expression, or executes a function that receives a text and returns a possible match.

  • fromPosition Object

    Optional. If specified, from which line/column wants to retrieve the content. Default value is { line: 1, column: 0 }. Use the same value received at to result as next call fromPosition to find all matches.

The function match receives three parameters:

Copy
match((node: TAST), { line: Number, column: Number }, (isVisible: Boolean));

It returns null if nothing is found or:

  • value TAST with the matching TAST,

  • isVisible Boolean false if the found tast is not visible or is inside an invisible entity

  • { from, to } Range range in which the TAST is contained

Unlike selectors for finding things inside Visible TAST, Raw TAST can only search by TAST. It receives a function called match that will matches tast.

The match function traverses all the TAST nodes in preorder, from the whole array with everything to the last string leaf. The match node is invoked once for each node until it finds a match by returning true.

If the match starts at fromPosition, it reconstructs the existing TAST tree and traverses all parent nodes until it arrives to the first node that starts at that position.

The selector returns the first TAST node that match returns true. It also returns the isVisible boolean, you must consider that a non invisible inline might be contained by a visible inline. If it is the case, and you match that inline, the selector will warn you that it is not visible with isVisible = false.

appendTerminalTAST

This REDUX action appends a TAST to the end of one terminal.

Note: Dispatches an action with the type @orion/terminals-helper/APPEND_TERMINAL_TAST which can be handled.
Usage
Copy
dispatch('appendTerminalTAST', { terminalId: String, tast: TAST });
  • terminalId String

    Required. It is the terminal identifier to add the TAST

  • tast TAST

    Required. It is the TAST to add to the Terminal

Example
Copy
const { textshot } = terminalTestUtils;
dispatchersHelper.dispatch('appendTerminalTAST', {
terminalId,
tast: ['A', { type: 'Inline', children: ['B'] }, 'C'],
});
expect(textshot(terminal)).toEqual([`>ABC`]);

writeTerminalTAST

This REDUX action writes a TAST to an arbitrary position of the terminal. It simulates the behaviour of a user writing a TAST into a terminal. Cursor is moved for each visible character and content is set.

Note: Dispatches an action with the type @orion/terminals-helper/WRITE_TERMINAL_TAST which can be handled.
Usage
Copy
dispatch('writeTerminalTAST', {
terminalId: String,
tast: TAST,
position: { line: Number, column: Number },
mode: 'overwrite' | 'insert',
});
  • terminalId String

    Required. It is the terminal identifier where to add the TAST.

  • tast TAST

    Required. It is TAST to write in the terminal.

  • position String

    Optional. It is the position where to start writing TAST. By default is the current cursor position.

  • mode 'overwrite' | 'insert'

    Optional. It is the mode in which to write TAST. By default is the current terminals mode.

Note that the cursor position is always moved, it includes when position is specified.

Example
Copy
const { textshot } = terminalTestUtils;
dispatchersHelper.dispatch('writeTerminalTAST', {
terminalId,
tast: ['ABC\n123'],
});
expect(textshot(terminal)).toEqual(['>ABC', '123█']);

writeTerminalChars

This REDUX action writes a text into the terminal. This is the action called by the terminal itself when the user writes into the terminal, and it is designed to write small chunks of characters. This action always writes from the current cursor position, moves the cursor position accordingly, and uses the current terminals mode. It also accepts a replacing argument, which is the number of characters that must replaced with the new characters. It can be used to perform sligtly different operations like delete character.

Note: Dispatches an action with the type @orion/terminals-helper/WRITE_TERMINAL_CHARS which can be handled.
Usage
Copy
dispatch('writeTerminalChars', {
  terminalId: String,
  chars: String,
  replacing: Number,
});
  • terminalId String

    Required. It is the terminal to modify.

  • chars String

    Optional. Characters to write into the terminal.

  • replacing Number

    Optional. It is the number of characters to be replaced.

Note that either chars or replacing must be present.

Examples
Copy
const { textshot } = terminalTestUtils;
dispatchersHelper.dispatch('writeTerminalChars', {
terminalId,
chars: 'ABC\n123',
});
expect(textshot(terminal)).toEqual(['>ABC', '123█']);
Copy
const { textshot } = terminalTestUtils;
dispatchersHelper.dispatch('writeTerminalChars', {
terminalId,
chars: 'ABC',
replacing: 1,
});
expect(textshot(terminal)).toEqual(['ABC█']);

deleteTerminalRange

A REDUX action that deletes content from the terminal from a specified range. This operation does not remove lines and does not moves content from one line to another.

Note: Dispatches an action with the type @orion/terminals-helper/DELETE_TERMINAL_RANGE which can be handled
Usage
Copy
dispatch('writeTerminalChars', {
  terminalId: String,
  from: { line: Number, column: Number },
  to: { line: Number, column: Number },
  mode: 'overwrite' | 'insert',
});
  • terminalId String

    Required. It is the terminal to modify.

  • from String

    Required. Begin of the range to be removed.

  • to String

    Required. End of the range to be removed. It must be equals or greater than from.

  • mode 'overwrite' | 'insert'

    Required. In which mode the content will be removed.

Note that this operation behaves like the user deleting characters directly but in the specified mode and in the specified range.

Example
Copy
const { textshot } = terminalTestUtils;
const { dispatch } = dispatchersHelper;

dispatch('writeTerminalChars', { terminalId, chars: 'ABC\n123' });
dispatchersHelper.dispatch('deleteTerminalRange', {
  terminalId,
  from: { line: 1, column: 4 },
  to: { line: 2, column: 2 },
  mode: 'insert',
});

expect(textshot(terminal)).toEqual(['>AB', '23█']);

spliceTerminalTAST

This REDUX action replaces any arbitrary TAST by any other TAST. Its name takes place from the Javascript array splice operation. It can be used to insert, remove and replace content from the terminal.

If no new TAST is specified it removes content from the terminal. If TAST is specified and the range is empty, it inserts content in the from position. If the range is not empty and there is new TAST, it replaces existing TAST in the range by the new one. The new content can be shorter or larger than the existing one. If any of these two cases happen all the content of the terminal is moved. This is the only method to insert and remove lines.

This action can be understood in a sequence of the next two following operations:

  1. Remove existing TAST in the corresponding range

  2. Insert new TAST in the from position.

Although it does not move the cursor position, it may alter its position. This is the case when content has been removed and the cursor is left outside the resulting terminal dimensions. The cursor is moved vertically until fit into the terminal.

Like other content actions, spliceTerminalTAST does not scrolls the viewport. Although, if the terminal content has shrunk and the viewport is outside it, it is scrolled to the terminal again.

Note: Dispatches an action with the type @orion/terminals-helper/SPLICE_TERMINAL_TAST which can be handled.
Usage
Copy
dispatchersHelper.dispatch('spliceTerminalTAST', {
  terminalId: String,
  tast: TAST,
  from: { line: Number, column: Number },
  to: { line: Number, column: Number },
});
  • terminalId String

    Required. It is the terminal to modify.

  • tast String

    Optional. The TAST that wants to insert.

  • from Position

    Optional. It is the position in which TAST must be inserted and start of the range of TAST to remove. The default value is { line: 1, column: 1 }

  • to Position

    Optional. It is the end of the range of existing TAST that must be removed. The default value is { line: lastLine + 1, column: 0 }

Example
Copy
const { textshot } = terminalTestUtils;

dispatchersHelper.dispatchersHelper.dispatch('spliceTerminalTAST', {
  terminalId,
  tast: ['ABC\n123'],
});

expect(textshot(terminal)).toEqual(['A█C', '123']);

findTerminalPreviousSOM

This REDUX selector returns the previous SOM position from the current cursor position of the terminal according its host configuration.

Usage
Copy
findTerminalPreviousSOM(state: State, { terminalId: String }): null | {
  value: SOM,
  from: { line: Number, column: Number },
  to: { line: Number, column: Number },
}
  • state State

    Required. It is the global application state

  • terminalId String

    Required. It is the terminal to get the cryptic.

Returns the corresponding range.

Example
Copy
const findTerminalPreviousSOM = selectorsHelper.make('findTerminalPreviousSOM');
const { getState } = storeHelper;

const { to: somPosition } = findTerminalPreviousSOM(getState(), { terminalId });

getTerminalCryptic

This REDUX selector extracts the cryptic text in the current cursor position of the terminal according its host configuration.

Usage
Copy
getTerminalCryptic(state: State, { terminalId: String }): String
  • state State

    Required. It is the global application state

  • terminalId String

    Required. It is the terminal to get the cryptic.

Returns the corresponding cryptic.

Example
Copy
const getTerminalCryptic = selectorsHelper.make('getTerminalCryptic');
const { getState } = storeHelper;
const commandToExecute = getTerminalCryptic(getState(), { terminalId });
dispatchersHelper.dispatch('sendTerminalEntry', {
terminalId,
entry: { cryptic: commandToExecute },
});