Skip to content

Commit

Permalink
fix: address horizontal scrolling issues (#20)
Browse files Browse the repository at this point in the history
* fix: address horizontal scrolling issues

and mutating input handlers not triggering display refreshes

Signed-off-by: Steven E Wright <StevenEWright@users.noreply.github.com>

* fix: missing docs

Signed-off-by: Steven E Wright <StevenEWright@users.noreply.github.com>

---------

Signed-off-by: Steven E Wright <StevenEWright@users.noreply.github.com>
  • Loading branch information
StevenEWright committed Aug 12, 2023
1 parent 6b6d093 commit 995eda7
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 131 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<a href="https://cdn.jsdelivr.net/npm/@kullna/editor/dist/kullna-editor.min.js">
<img src="https://img.shields.io/badge/CDN-JSDelivr-2aa198" alt="CDN">
</a>
<a href="https://www.npmjs.com/package/kullna/editor">
<a href="https://www.npmjs.com/package/@kullna/editor">
<img src="https://img.shields.io/npm/v/@kullna/editor?color=dc322f" alt="npm">
</a>
<img src="https://deno.bundlejs.com/?q=@kullna/editor&badge=small" alt="npm bundle size">
Expand Down
12 changes: 5 additions & 7 deletions demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
box-shadow:
rgba(0, 0, 0, 0.4) 0px 2px 4px,
rgba(0, 0, 0, 0.3) 0px 7px 13px -3px,
rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
var(--sd-yellow) 0px -5px 0px;
font-family: 'KawkabMonoRegular', 'Source Code Pro', monospace;
font-size: 12px;
font-weight: 400;
Expand Down Expand Up @@ -135,7 +135,7 @@ <h1 align="center">@kullna/editor Demos</h1>
><img src="https://www.kullna.org/brand/logo.svg" width="150"
/></a>
</p>
<h1 align="center">@kullna/editor</h1>
<h1 align="center">@kullna/editor Demos</h1>
<h3 align="center">A small but feature-rich code editor for the web</h3>
<p align="center"><a href="/">Home</a></p>
<hr />
Expand All @@ -156,13 +156,13 @@ <h3 align="center">A small but feature-rich code editor for the web</h3>
</main>

<!-- For local testing, comment this: -->
<!-- <script src="https://cdn.jsdelivr.net/npm/@kullna/editor/dist/kullna-editor.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/@kullna/editor/dist/kullna-editor.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.8.0/build/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>

<script type="module">
// For local testing, uncomment this:
import * as KullnaEditor from './dist/kullna-editor.esm.js';
// import * as KullnaEditor from './dist/kullna-editor.esm.js';

function demoWithOptions(parentSelector, id, titleText, options, defaultValue) {
const parent = document.querySelector(parentSelector);
Expand Down Expand Up @@ -214,9 +214,7 @@ <h3 align="center">A small but feature-rich code editor for the web</h3>
`<div id="editor"><\/div>
...
<script>
const editorElement = document.querySelector('#editor')
const editor = KullnaEditor.createEditor(editorElement, {
const editor = KullnaEditor.createEditor('#editor', {
language: 'javascript',
highlight: hljs.highlightElement,
gutter: {
Expand Down
54 changes: 0 additions & 54 deletions generate_docs_index.js

This file was deleted.

1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
* 3. **{@link Processors}**:
* - {@link Processors.InputProcessor}: Processes the editor's input.
* - {@link Processors.DefaultProcessors}: Contains default input processors.
* 4. **{@link Text} Management**:
* - {@link Text.TextDocument}: Represents the editor's text.
* - {@link Text.TextEditorViewKeyboardEvent}: Represents the editor's keyboard events.
*
Expand Down
2 changes: 2 additions & 0 deletions src/internals/gutter/gutter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ function createHighlightElement(opts: GutterOptions): HTMLElement {
highlight.style.position = 'absolute';
highlight.style.left = '0px';
highlight.style.right = '0px';
highlight.style.zIndex = '99';
highlight.innerHTML = '&nbsp;';
return highlight;
}
Expand All @@ -234,6 +235,7 @@ function createGutterElement(opts: GutterOptions): HTMLElement {

gutter.style.position = 'absolute';
gutter.style.top = '0px';
gutter.style.zIndex = '50';
gutter.style.bottom = '0px';
if (opts.dir === 'ltr') {
gutter.style.left = '0px';
Expand Down
96 changes: 50 additions & 46 deletions src/internals/pipeline/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,57 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
/**
* @packageDocumentation # Input Processors
*
* Input processors are functions that control how
* the editor responds to textual input. They have the ability to mutate the editor's code and
* selection state, and to prevent the default behavior from executing.
* Input processors are specialized functions designed to control and manage
* how the editor responds to textual input. By enabling developers to define
* and adjust the behavior of the editor in response to various inputs, processors
* offer modularity and easy customization.
*
* Processors can be used to insert additional text and control the cursor; which makes implementing
* a wide variety of behaviors possible. For example, the default tab processor inserts spaces when
* the tab key is pressed, and the default newline processor inserts a newline and indents the
* cursor to the same level as the previous line.
* This flexibility enables a wide range of editor behaviors. For instance, the default tab
* processor inserts spaces when the tab key is pressed, while the newline processor
* adds new lines and appropriately indents them.
*
* **There are some functions the editor performs that are not controlled by input processors:**
* ## Not Controlled by Input Processors
* **There are specific editor functionalities that remain autonomous and aren't influenced by input processors:**
* - **Undo/Redo**: Managed internally by the editor's undo/redo stack. The combinations `Ctrl+Z` and `Ctrl+Shift+Z` trigger these actions.
* - **Cut/Paste**: `Ctrl+X` and `Ctrl+V` are exclusively reserved for these actions.
*
* - **Undo/Redo**: The editor maintains its own undo/redo stack and handles undo/redo events
* internally, and it captures the `Ctrl+Z` and `Ctrl+Shift+Z` key combinations to trigger these.
* - **Cut/Paste**: The editor captures the `Ctrl+X` and `Ctrl+V` key combinations to trigger these.
* Apart from these, most interactive editor functionalities can be tailored using input processors.
*
* But most other functions are controlled by input processors.
* ## How Processors Work
* Processors are organized in a sequence termed as a **pipeline**. If a processor "claims to have handled the event" (by marking the event as "handled" with `args.handled = true`),
* subsequent processors in the pipeline are bypassed, overriding the default behavior.
*
* We call a sequence of input processors a **pipeline**. The pipeline is executed in order, and
* each processor has the ability to prevent the default behavior from executing. If a processor
* claims to have handled the event, the pipeline stops executing and the default behavior is
* prevented. If a processor does not claim to have handled the event, the next processor in the
* pipeline is executed.
* A processor's primary function is to transform the document and selection.
* They take as a parameter the current document and the input event details,
* and may return a new document or `undefined`/`null` (either of which indicate the processor
* did not modify the document).
*
* There are three built-in types of input processors that are used in the default configuration of the
* keydown pipeline:
* {@link Text.TextDocument}s are immutable, so processors must return a new document
* if they modify the document. If a processor does not modify the document, it
* should return `undefined` or `null`.
*
* - `BracketProcessor` - Handles bracket pairs, such as `()`, `[]`, and `{}`.
* - `TabProcessor` - Handles inserting the correct tab character(s) when the `Tab` key is pressed, and
* handles the `Shift+Tab` key combination. Works for both a cursor and a range selection
* (including ranges over multiple-lines).
* - `EnterProcessor` - Handles inserting a newline and indenting the cursor to the same level as the
* previous line.
* The {@link Text.TextDocument} API can be used to create new {@link Text.TextDocument} instances.
* These APIs are cursor-centric and allow for easy manipulation of the document.
*
* Please see {@link DefaultProcessors} for more information.
* ## Default Processors
* The editor's default configuration includes three input processors:
* - `BracketProcessor`
* - `TabProcessor`
* - `EnterProcessor`
*
* **All Input Processors receive two parameters:**
* Detailed explanations are available at {@link DefaultProcessors}.
*
* - `document: TextDocument` - Allows processors to produce a modified document.
* - `args: InputProcessorArgs` - Arguments which include `event: TextEditorViewKeyboardEvent` which
* contains the key that was pressed, the key code, and other information.
* ## Processor Parameters
* Every input processor accepts two parameters:
* - `document: TextDocument` - Allowing processors to produce an altered document.
* - `args: InputProcessorArgs` - Contains the input event details, encapsulated within `event: TextEditorViewKeyboardEvent`.
*
* Please see {@link TextDocument} for more information about the text document API. Please see
* {@link InputProcessorArgs} for more information about the arguments passed to processors.
* More insights are available at {@link TextDocument} and {@link InputProcessorArgs}.
*
* ## Developing an Input Processor
* ## Developing a Custom Input Processor
* Custom input processors provide an avenue for developers to implement unique or specific behaviors. Imagine needing an input processor that aids in auto-formatting code or one that integrates specific keyboard shortcuts.
*
* **Here's a template for creating an input processor.** The template is the same no matter which
* type of processor you're creating:
* Here's a foundational template for crafting an input processor:
*
* ```js
* import {
Expand All @@ -74,35 +77,36 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
* } from '@kullna/editor';
*
* export interface MyOptions {
* // ... Create an interface for your options ...
* // Define your option parameters here...
* }
*
* // Create a function that returns an InputProcessor:
* // Function to craft a custom InputProcessor:
* export function createMyProcessor(options: MyOptions): InputProcessor {
* return (document: TextDocument, args: InputProcessorArgs): TextDocument | undefined | null => {
* // ... Use the Editor to manipulate the code and selection ...
* // ... If you've handled the event, mark it as handled:
* // Manipulate code and selection using the Editor API...
* // Mark the event as "handled" if addressed:
* args.handled = true;
* // Here, returning the document after typing a newline:
* return document.type('\n');
* // ... If you still want the event to be handled by the editor, leave it as false...
* // You can also return undefined or null based on specific conditions.
* // This can influence the subsequent behavior of the editor.
* };
* }
* ```
* (Note: In the above template, the processor can return `TextDocument`, `undefined`, or `null`. The choice of return type can influence how subsequent processors or default behaviors are triggered.)
*
* Once you've created your input processor, you can register it with the editor when you create it:
* After creating your input processor, register it with the editor:
*
* ```js
* import {
* createMyProcessor
* } from './my-processor';
* ...
* const editor = KullnaEditor.createEditor(element, {
* // ... Editor options ...
* language = 'javascript',
* // Configuration...
* language: 'javascript',
* highlight: hljs.highlightElement,
* // ... Register your processor ...
* keydown: [ createMyProcessor({ /* options * / }) ]
* }
* keydown: [ createMyProcessor({ options }) ]
* });
* ```
*/
Expand Down
13 changes: 9 additions & 4 deletions src/internals/text_editor/keyboard_event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
* # Keyboard Events
*
* When a keyboard event is fired, it is wrapped in a `TextEditorViewKeyboardEvent` object. This
* object provides a more convenient interface for dealing with keyboard events - which
* object provides a more convenient interface for dealing with keyboard events - which avoids the
* need to perform string comparisons on key codes in other parts of the code.
*
* This class is used to wrap a keyboard event and provide a more convenient interface for dealing
* with it. It avoids the need to perform string comparisons on key codes in other parts of the
* code.
* Also, it makes the event immutable, which is important because the event is passed to multiple
* handlers. If the event were mutable, then one handler could modify the event in a way that
* affects other handlers.
*
* These event details can be used when developing an Input Processor to determine which key was
* pressed. See: {@link Processors}.
*/
export class TextEditorViewKeyboardEvent {
constructor(private readonly event: KeyboardEvent) {}
Expand Down Expand Up @@ -102,6 +106,7 @@ export class TextEditorViewKeyboardEvent {
* contents of the editor directly.
*/
get isMutatingInput(): boolean {
if (this.keyCode === 'ENTER') return true;
if (this.keyCode === 'BACKSPACE') return true;
return (
!this.isUndo &&
Expand Down
3 changes: 2 additions & 1 deletion src/internals/text_editor/selection_bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ export class SelectionBridge {
const newDocumentTextContent = textDocument.text.replaceAll('\r\n', '\n');

if (initialTextContent !== newDocumentTextContent) {
throw new Error('Text content does not match');
this.element.textContent = newDocumentTextContent;
// throw new Error('Text content does not match');
}
let characterCount = 0;

Expand Down
5 changes: 2 additions & 3 deletions src/internals/text_editor/text_document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
* You can use the factory methods to produce new Text Document instances with modified content or
* changed selection.
*
* When you are done creating a `TextDocument`, you can update the `TextEditorView`'s, document.
*
* @see {@link TextEditorView} . {@link TextEditorView#document}
* Text Document methods may be used when developing an Input Processor to create modifications of
* the original document and return them. See: {@link Processors}.
*/
export class TextDocument {
/**
Expand Down
Loading

0 comments on commit 995eda7

Please sign in to comment.