Skip to content

Commit

Permalink
refactor: separate the highlight view (#16)
Browse files Browse the repository at this point in the history
fix: undo/redo improper counting
fix: backspace not considered "mutating"
feat: npm run website:dev
fix: input processor docs
feat: re-introduce highlight throttling

Signed-off-by: Steven E Wright <StevenEWright@users.noreply.github.com>
  • Loading branch information
StevenEWright committed Aug 12, 2023
1 parent 4c1d7a3 commit 3234346
Show file tree
Hide file tree
Showing 17 changed files with 381 additions and 197 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@
"*.md"
],
"scripts": {
"website:gen": "./website",
"website:gen": "./website gen",
"website:gendev": "./website gendev",
"website": "npm run website:gen && cd ./docs/ && web-dev-server --app-index ./index.html --node-resolve --open",
"website:dev": "npm run website:gendev && cd ./docs/ && web-dev-server --app-index ./index.html --node-resolve --open",
"document": "npm run document:gen && cd ./docs/ && web-dev-server --app-index ./index.html --node-resolve --open",
"document:dev": "npm run document:gendev && cd ./docs/ && web-dev-server --app-index ./index.html --node-resolve --open",
"document:gen": "node generatePackageMetadata.js && typedoc ./src/index.ts --disableSources --hideGenerator --darkHighlightTheme solarized-dark --lightHighlightTheme solarized-dark --titleLink \"/modules.html\"",
"document:gendev": "node generatePackageMetadata.js && typedoc ./src/docs_index.ts --excludePrivate false --hideGenerator --darkHighlightTheme solarized-dark --lightHighlightTheme solarized-dark --titleLink \"/modules.html\"",
"size": "size-limit",
"lint:ts": "eslint ./src/**/*.ts",
"lint:prettier": "prettier . --check",
Expand Down
31 changes: 31 additions & 0 deletions src/docs_index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* Kullna Editor - A small but feature-rich code editor for the web
Copyright (C) 2022-2023 The Kullna Programming Language Project
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */

/**
* @packageDocumentation # @kullna/editor Documentation (Dev Version)
*
* Welcome to the documentation for the Kullna Editor!
*
* Please see: [Test Page](/test.html)
*/
export {Options} from './options';
export {KullnaEditor} from './kullna_editor';
export {EditorOptions} from './internals/editor_options';
export {Editor} from './internals/editor';
export {UndoRedoManager} from './internals/undo_redo_manager';
export * as Text from './internals/text_editor/docs_index';
export * as Gutters from './internals/gutter/docs_index';
export * as Processors from './internals/pipeline/docs_index';
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
* - {@link Processors.DefaultProcessors}: Contains default input processors.
* 4. **{@link Text} Management**:
* - {@link Text.TextDocument}: Represents the editor's text.
* - {@link Text.TextEditorKeyboardEvent}: Represents the editor's keyboard events.
* - {@link Text.TextEditorViewKeyboardEvent}: Represents the editor's keyboard events.
*
* With these extension points, you can customize the way the editor handles input and what it does
* when the text changes. You can also customize the gutter to your needs, including adding
Expand Down
46 changes: 23 additions & 23 deletions src/internals/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */

import {TextDocument, TextEditorKeyboardEvent} from './text_editor';
import {TextDocument, TextEditorViewKeyboardEvent} from './text_editor';
import {type InputProcessor} from './pipeline';
import {Gutter} from './gutter/gutter';
import {InputProcessorArgs} from './pipeline/input_processor';
Expand Down Expand Up @@ -112,23 +112,23 @@ export class Editor

// ---------------- Editor ----------------

/** @inheritdoc */
/** @inheritDoc */
get spellcheck(): boolean {
return this.view.spellchecking;
}
set spellcheck(spellcheck: boolean) {
this.view.spellchecking = spellcheck;
}

/** @inheritdoc */
/** @inheritDoc */
get language(): string {
return this.view.language;
}
set language(language: string) {
this.view.language = language;
}

/** @inheritdoc */
/** @inheritDoc */
get dir(): 'ltr' | 'rtl' {
return this.view.dir;
}
Expand All @@ -139,7 +139,7 @@ export class Editor
}
}

/** @inheritdoc */
/** @inheritDoc */
get highlightedLine(): number {
return this.gutter ? this.gutter.highlightedLine : -1;
}
Expand All @@ -149,7 +149,7 @@ export class Editor
}
}

/** @inheritdoc */
/** @inheritDoc */
invalidateGutterLine(line: number): void {
if (this.gutter) {
this.gutter.updateLineNumber(line);
Expand All @@ -170,69 +170,69 @@ export class Editor
this.notifyPotentiallyChanged(true);
}

/** @inheritdoc */
/** @inheritDoc */
onUpdate(callback: (code: string) => void): void {
this.options.onUpdate = callback;
}

/** @inheritdoc */
/** @inheritDoc */
onSelectionFocusChanged(callback: (document: TextDocument) => void): void {
this.options.onSelectionFocusChanged = callback;
}

/** @inheritdoc */
/** @inheritDoc */
destroy(): void {
this.view.destroy();
}

// ---------------- EditorStateSerializable ----------------

/** @inheritdoc */
/** @inheritDoc */
currentUndoRedoState(): TextDocument {
return this.view.cachedDocument;
}

/** @inheritdoc */
/** @inheritDoc */
// skipcq: JS-0105: Class methods should utilize this
undoRedoStatesAreEquivalent(a: TextDocument, b: TextDocument): boolean {
return a.perceptuallyEquals(b);
}

/** @inheritdoc */
/** @inheritDoc */
restoreUndoRedoState(document: TextDocument) {
this.view.setDocumentUnchecked(document);
}

// ---------------- EditorInputEventHandler ----------------

/** @inheritdoc */
/** @inheritDoc */
selectionChanged(): void {
if (this.options.onSelectionFocusChanged) {
this.options.onSelectionFocusChanged(this.view.cachedDocument);
}
}

/** @inheritdoc */
/** @inheritDoc */
contentChanged(): void {
this.notifyPotentiallyChanged();
}

/** @inheritdoc */
/** @inheritDoc */
scroll(element: HTMLElement) {
if (!this.gutter) return;
// this.gutter.element.style.top = `-${element.scrollTop}px`;
this.gutter.setScrollTop(element.scrollTop);
}

/** @inheritdoc */
/** @inheritDoc */
cut(event: ClipboardEvent): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const originalEvent = (event as any).originalEvent ?? event;
originalEvent.clipboardData.setData('text/plain', this.view.cachedDocument.selectedText);
this.view.setDocumentUnchecked(this.view.cachedDocument.deleteSelection());
}

/** @inheritdoc */
/** @inheritDoc */
paste(event: ClipboardEvent): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const originalEvent = (event as any).originalEvent ?? event;
Expand All @@ -241,28 +241,28 @@ export class Editor
this.view.cachedDocument = this.view.cachedDocument.insertText(text, true, true);
}

/** @inheritdoc */
keydown(event: TextEditorKeyboardEvent): boolean {
/** @inheritDoc */
keydown(event: TextEditorViewKeyboardEvent): boolean {
if (this.options.keydownPipeline) {
return this.processPipeline(this.options.keydownPipeline, {handled: false, event});
}
return false;
}

/** @inheritdoc */
keyup(event: TextEditorKeyboardEvent): boolean {
/** @inheritDoc */
keyup(event: TextEditorViewKeyboardEvent): boolean {
if (this.options.keyupPipeline) {
return this.processPipeline(this.options.keyupPipeline, {handled: false, event});
}
return false;
}

/** @inheritdoc */
/** @inheritDoc */
undo(): void {
this.undoRedoManager.undo();
}

/** @inheritdoc */
/** @inheritDoc */
redo(): void {
this.undoRedoManager.redo();
}
Expand Down
38 changes: 38 additions & 0 deletions src/internals/gutter/docs_index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* Kullna Editor - A small but feature-rich code editor for the web
Copyright (C) 2022-2023 The Kullna Programming Language Project
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */

/**
* @packageDocumentation # Gutters (Dev Version)
*
* The Gutter handles creating and maintaining
* the DOM elements within the gutter. This approach anticipates future optimization
* where we aim to manage gutter elements more efficiently by reusing them, rather than
* creating new ones every time the line count in the editor changes. This strategy
* requires the editor to have complete control over these gutter elements. Presently,
* the gutter also determines the line's location within the editor, crucial for line
* numbers and highlighting - however, this source of truth will shift to the internals
* of the selection bridge in the future, as it knows the line's location within the
* editor already, and defininitively.
*
* Currently line highlighting is handled by the gutter, but this will change in the future
* when we have a better, more flexible, and more performant way of tracking line heights
* and positions.
*/

export {GutterLineElement} from './line';
export {GutterCustomizer} from './customizer';
export {Gutter} from './gutter';
export {GutterOptions} from './options';
23 changes: 23 additions & 0 deletions src/internals/pipeline/docs_index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Kullna Editor - A small but feature-rich code editor for the web
Copyright (C) 2022-2023 The Kullna Programming Language Project
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */

/** @packageDocumentation # Input Processors (Dev Version) */
export {type InputProcessor, type InputProcessorArgs} from './input_processor';
export {type NewlineProcessorOptions, newlineProcessor} from './newline_processor';
export {type TabProcessorOptions, tabProcessor} from './tab_processor';
export {type bracketProcessor} from './bracket_processor';

export {DefaultProcessors} from './index';
56 changes: 32 additions & 24 deletions src/internals/pipeline/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,48 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
/**
* @packageDocumentation # Input Processors
*
* Input processors handle keyboard input from the user; like what happens when the tab or enter
* keys are pressed.
* 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.
*
* 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. 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.
* cursor to the same level as the previous line.
*
* **There are some functions the editor performs that are not controlled by input processors:**
*
* - **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.
*
* **There are three types of input processors:**
* But most other functions are controlled by input processors.
*
* 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.
*
* There are three built-in types of input processors that are used in the default configuration of the
* keydown pipeline:
*
* - `BracketProcessor` - Invoked whenever the user presses a key while the editor is focused and the
* key is not a special key (e.g. `Enter`, `Tab`, `Backspace`, etc.).
* - `TabProcessor` - Invoked whenever the user presses the `Tab` key while the editor is focused.
* - `EnterProcessor` - Invoked whenever the user presses the `Enter` key while the editor is focused.
* - `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.
*
* Please see {@link Processors} for more information.
* Please see {@link DefaultProcessors} for more information.
*
* **All Input Processors receive two parameters:**
*
* - `editor: TextEditorView` - Allows processors to mutate the document or the cursor.
* - `args: InputProcessorArgs` - Arguments which include `event: TextEditorKeyboardEvent` which
* - `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.
*
* Please see {@link TextEditorView} for more information about the editor API. Please see
* 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.
*
* ## Developing an Input Processor
Expand All @@ -59,7 +69,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
* ```js
* import {
* InputProcessorArgs,
* TextEditorView,
* TextDocument,
* InputProcessor
* } from '@kullna/editor';
*
Expand All @@ -69,12 +79,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
*
* // Create a function that returns an InputProcessor:
* export function createMyProcessor(options: MyOptions): InputProcessor {
* return (editor: TextEditorView, args: InputProcessorArgs): boolean => {
* return (document: TextDocument, args: InputProcessorArgs): TextDocument | undefined | null => {
* // ... Use the Editor to manipulate the code and selection ...
* editor.type('\n');
* // ... If you've handled the event, return true ...
* return true;
* // ... If you still want the event to be handled by the editor, return false ...
* // ... If you've handled the event, mark it as handled:
* args.handled = true;
* return document.type('\n');
* // ... If you still want the event to be handled by the editor, leave it as false...
* };
* }
* ```
Expand All @@ -90,10 +100,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
* // ... Editor options ...
* language = 'javascript',
* highlight: hljs.highlightElement,
* enterProcessor: {
* replacement: createMyProcessor({
* // ... Pass your options here ...
* })
* // ... Register your processor ...
* keydown: [ createMyProcessor({ /* options * / }) ]
* }
* });
* ```
Expand Down
Loading

0 comments on commit 3234346

Please sign in to comment.