From 7a7c7a0c90e57c8122fecea64f247fcef24a4f6d Mon Sep 17 00:00:00 2001 From: Zeyad Ahmad Aql Date: Sun, 16 Apr 2023 13:00:00 +0200 Subject: [PATCH 1/6] feat: add newline command --- css/core.less | 4 ++++ src/addons/definitions-metadata.ts | 1 + src/addons/math-ml.ts | 4 ++++ src/core-atoms/newline.ts | 35 ++++++++++++++++++++++++++++++ src/core-definitions/symbols.ts | 8 +++++++ src/core/atom-class.ts | 1 + src/core/atom.ts | 2 ++ 7 files changed, 55 insertions(+) create mode 100644 src/core-atoms/newline.ts diff --git a/css/core.less b/css/core.less index 617ab019b..7fdc4c016 100644 --- a/css/core.less +++ b/css/core.less @@ -210,6 +210,10 @@ padding: 0 0.5em; } +.ML__newline { + display: block; +} + .ML__frac-line { width: 100%; min-height: 1px; diff --git a/src/addons/definitions-metadata.ts b/src/addons/definitions-metadata.ts index 5a84422d5..ddbeaaeb9 100644 --- a/src/addons/definitions-metadata.ts +++ b/src/addons/definitions-metadata.ts @@ -1040,6 +1040,7 @@ metadata( RARE, '\\unicode{"203A}$0{1em}\\unicode{"2039}' ); +metadata('Spacing', ['\\\\'], COMMON); // New Line metadata( 'Punctuation', [ diff --git a/src/addons/math-ml.ts b/src/addons/math-ml.ts index 25ced6f0d..ee2963da8 100644 --- a/src/addons/math-ml.ts +++ b/src/addons/math-ml.ts @@ -728,6 +728,10 @@ function atomToMathML(atom, options): string { case 'overlap': break; + case 'newline': + result += ''; + break; + case 'overunder': overscript = atom.above; underscript = atom.below; diff --git a/src/core-atoms/newline.ts b/src/core-atoms/newline.ts new file mode 100644 index 000000000..53ee32fa7 --- /dev/null +++ b/src/core-atoms/newline.ts @@ -0,0 +1,35 @@ +import { Atom, AtomJson } from '../core/atom-class'; +import { Box } from '../core/box'; +import { Context } from '../core/context'; + +import type { ParseMode, Style } from '../public/core-types'; +import type { GlobalContext } from '../core/types'; + +export class NewLineAtom extends Atom { + constructor(command: string, context: GlobalContext, style: Style) { + super('newline', context, { command, style }); + this.skipBoundary = true; + } + + static fromJson(json: AtomJson, context: GlobalContext): NewLineAtom { + return new NewLineAtom(json.command, context, json as any); + } + + toJson(): AtomJson { + return super.toJson(); + } + + render(context: Context): Box | null { + const box = new Box(null, { + classes: 'ML__newline', + type: '', + }); + box.caret = (this.caret as ParseMode) ?? null; + this.bind(context, box); + return box; + } + + serialize(): string { + return '\\\\'; + } +} diff --git a/src/core-definitions/symbols.ts b/src/core-definitions/symbols.ts index ce94f1401..3ad105003 100644 --- a/src/core-definitions/symbols.ts +++ b/src/core-definitions/symbols.ts @@ -1,3 +1,4 @@ +import { NewLineAtom } from '../core-atoms/newline'; import { SpacingAtom } from '../core-atoms/spacing'; import { @@ -680,6 +681,13 @@ defineFunction(['!', ',', ':', ';', 'enskip', 'enspace', 'quad', 'qquad'], '', { new SpacingAtom(command, style, context), }); +// New line +newSymbols('\\\\', 'newline'); +defineFunction('\\', '', { + createAtom: (command, context, style) => + new NewLineAtom(command, context, style), +}); + // Punctuation newSymbols( [ diff --git a/src/core/atom-class.ts b/src/core/atom-class.ts index 9dc126bce..2b035d250 100644 --- a/src/core/atom-class.ts +++ b/src/core/atom-class.ts @@ -107,6 +107,7 @@ export type AtomType = | 'leftright' // Used by the `\left` and `\right` commands | 'line' // Used by `\overline` and `\underline` | 'macro' + | 'newline' // New line command: `\\` | 'subsup' // A carrier for a superscript/subscript | 'overlap' // Display a symbol _over_ another | 'overunder' // Displays an annotation above or below a symbol diff --git a/src/core/atom.ts b/src/core/atom.ts index 6445dd138..92ba9b702 100644 --- a/src/core/atom.ts +++ b/src/core/atom.ts @@ -30,6 +30,7 @@ import { SurdAtom } from '../core-atoms/surd'; import { TextAtom } from '../core-atoms/text'; import { TooltipAtom } from '../core-atoms/tooltip'; import { PromptAtom } from '../core-atoms/prompt'; +import { NewLineAtom } from '../core-atoms/newline'; import type { GlobalContext } from '../core/types'; export * from './atom-class'; @@ -69,6 +70,7 @@ export function fromJson( if (type === 'leftright') result = LeftRightAtom.fromJson(json, context); if (type === 'line') result = LineAtom.fromJson(json, context); if (type === 'macro') result = MacroAtom.fromJson(json, context); + if (type === 'newline') result = NewLineAtom.fromJson(json, context); if (type === 'subsup') result = SubsupAtom.fromJson(json, context); if (type === 'overlap') result = OverlapAtom.fromJson(json, context); if (type === 'overunder') result = OverunderAtom.fromJson(json, context); From 4972e84c97fc9025822240d1f2a568931451b00b Mon Sep 17 00:00:00 2001 From: Zeyad Ahmad Aql Date: Sun, 16 Apr 2023 13:55:36 +0200 Subject: [PATCH 2/6] feat: insert new line on enter Update keybindings-definitions --- src/editor/keybindings-definitions.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/editor/keybindings-definitions.ts b/src/editor/keybindings-definitions.ts index 5ef45f8e5..682ee982b 100644 --- a/src/editor/keybindings-definitions.ts +++ b/src/editor/keybindings-definitions.ts @@ -87,6 +87,9 @@ export const DEFAULT_KEYBINDINGS: Keybinding[] = [ }, // Complete the suggestion { key: '[Return]', ifMode: 'latex', command: 'complete' }, { key: '[Enter]', ifMode: 'latex', command: 'complete' }, + { key: '[Return]', ifMode: 'math', command: ['insert', '\\\\'] }, + { key: '[Enter]', ifMode: 'math', command: ['insert', '\\\\'] }, + { key: '[NumpadEnter]', ifMode: 'math', command: ['insert', '\\\\'] }, { key: 'shift+[Escape]', ifMode: 'latex', From 5b3c4d4da8d009363a211f12a42f9950198090cd Mon Sep 17 00:00:00 2001 From: Zeyad Ahmad Aql Date: Sun, 16 Apr 2023 13:55:51 +0200 Subject: [PATCH 3/6] fix a minor issue with tokenizing `\\` command --- src/core/atom-class.ts | 10 ++++++++++ src/editor-mathfield/mathfield-private.ts | 11 ++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/core/atom-class.ts b/src/core/atom-class.ts index 2b035d250..17d2a656d 100644 --- a/src/core/atom-class.ts +++ b/src/core/atom-class.ts @@ -559,6 +559,16 @@ export class Atom { return false; } + get isInArrayAtom(): boolean { + // eslint-disable-next-line @typescript-eslint/no-this-alias + let atom: Atom | undefined = this; + while (atom) { + if (atom.type === 'array') return true; + atom = atom.parent; + } + return false; + } + /** Return the parent editable prompt, if it exists */ get parentPrompt(): Atom | null { // eslint-disable-next-line @typescript-eslint/no-this-alias diff --git a/src/editor-mathfield/mathfield-private.ts b/src/editor-mathfield/mathfield-private.ts index 52cd5109e..4ec66f585 100644 --- a/src/editor-mathfield/mathfield-private.ts +++ b/src/editor-mathfield/mathfield-private.ts @@ -1071,11 +1071,12 @@ If you are using Vue, this may be because you are using the runtime-only build o if (options.scrollIntoView) this.scrollIntoView(); - if (s === '\\\\') { - // This string is interpreted as an "insert row after" command - addRowAfter(this.model); - } else if (s === '&') addColumnAfter(this.model); - else { + if (this.model.at(this.model.position).isInArrayAtom) { + if (s === '\\\\') { + // This string is interpreted as an "insert row after" command + addRowAfter(this.model); + } else if (s === '&') addColumnAfter(this.model); + } else { const savedStyle = this.style; ModeEditor.insert(this.mode, this.model, s, { style: this.model.at(this.model.position).computedStyle, From 663caf941aeacd145b4430da623a586c2afda733 Mon Sep 17 00:00:00 2001 From: Zeyad Ahmad Aql Date: Sun, 16 Apr 2023 13:56:17 +0200 Subject: [PATCH 4/6] fix: some atoms have left margin after newline atom --- src/core-atoms/newline.ts | 2 +- src/core/box.ts | 3 +++ src/core/types.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core-atoms/newline.ts b/src/core-atoms/newline.ts index 53ee32fa7..379f2c535 100644 --- a/src/core-atoms/newline.ts +++ b/src/core-atoms/newline.ts @@ -22,7 +22,7 @@ export class NewLineAtom extends Atom { render(context: Context): Box | null { const box = new Box(null, { classes: 'ML__newline', - type: '', + type: 'newline', }); box.caret = (this.caret as ParseMode) ?? null; this.bind(context, box); diff --git a/src/core/box.ts b/src/core/box.ts index c3634c7f3..fccf0ce91 100644 --- a/src/core/box.ts +++ b/src/core/box.ts @@ -759,6 +759,9 @@ function adjustType(root: Box | null): void { function applyInterAtomSpacing(root: Box | null, context: Context): void { forEachBox(root, (prevBox: Box, box: Box) => { const prevType: BoxType = prevBox?.type ?? 'none'; + + if (prevType === 'newline') return; + const table = box.isTight ? INTER_ATOM_TIGHT_SPACING[prevType] ?? null : INTER_ATOM_SPACING[prevType] ?? null; diff --git a/src/core/types.ts b/src/core/types.ts index 6e7e0130a..89482da9c 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -88,6 +88,7 @@ const BOX_TYPE = [ 'close', // > is a closing atom like ‘)’ 'punct', // > is a punctuation atom like ‘,’ 'inner', // > is an inner atom like ‘$$\frac12$$' + 'newline', // > is a new line box 'spacing', 'first', 'latex', From 7ba5b2342d72eed1327ad28015f1ca769c0f92fe Mon Sep 17 00:00:00 2001 From: Zeyad Ahmad Aql Date: Sun, 16 Apr 2023 14:26:33 +0200 Subject: [PATCH 5/6] replace action key with return key in virtual keyboard. Press return adds new line, pressing shift + return performs commit action. --- src/virtual-keyboard/data.ts | 6 +++--- src/virtual-keyboard/utils.ts | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/virtual-keyboard/data.ts b/src/virtual-keyboard/data.ts index 901ddab6b..c8d23d18a 100644 --- a/src/virtual-keyboard/data.ts +++ b/src/virtual-keyboard/data.ts @@ -135,7 +135,7 @@ export const LAYOUTS: Record = { '[separator-5]', '[left]', '[right]', - { label: '[action]', width: 1.0 }, + { label: '[return]', width: 1.0 }, ], ], }, @@ -372,7 +372,7 @@ export const LAYOUTS: Record = { '[left]', '[right]', - '[action]', + '[return]', ], ], }, @@ -660,7 +660,7 @@ export const LAYOUTS: Record = { width: 1.0, class: 'action hide-shift', }, - { label: '[action]', width: 1.0 }, + { label: '[return]', width: 1.0 }, ], ], }, diff --git a/src/virtual-keyboard/utils.ts b/src/virtual-keyboard/utils.ts index 242663f59..09c4859cb 100644 --- a/src/virtual-keyboard/utils.ts +++ b/src/virtual-keyboard/utils.ts @@ -177,7 +177,7 @@ function alphabeticLayout(): NormalizedVirtualKeyboardLayout { '[.]', '[left]', '[right]', - { label: '[action]', width: 1.5 }, + { label: '[return]', width: 1.5 }, ]); return { @@ -735,7 +735,8 @@ const KEYCAP_SHORTCUTS: Record> = { }, '[return]': { class: 'action', - command: ['performWithFeedback', 'commit'], + insert: '\\\\', + shift: { command: ['performWithFeedback', 'commit'] }, width: 1.5, label: '', }, From 5adac7f92f67c977ab0713c44d24b70c18529996 Mon Sep 17 00:00:00 2001 From: Zeyad Ahmad Aql Date: Tue, 18 Apr 2023 08:55:25 +0200 Subject: [PATCH 6/6] remove \\\\ `newSymbols` function --- src/core-definitions/styling.ts | 7 +++++++ src/core-definitions/symbols.ts | 8 -------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/core-definitions/styling.ts b/src/core-definitions/styling.ts index 4180e9305..e1fea8954 100644 --- a/src/core-definitions/styling.ts +++ b/src/core-definitions/styling.ts @@ -26,6 +26,7 @@ import type { import { GlobalContext, PrivateStyle } from '../core/types'; import { latexCommand } from '../core/tokenizer'; import { atomsBoxType } from '../core/box'; +import { NewLineAtom } from '../core-atoms/newline'; defineFunction('mathtip', '{:math}{:math}', { createAtom: ( @@ -811,6 +812,12 @@ defineFunction('mspace', '{width:glue}', { ), }); +// New line +defineFunction('\\', '', { + createAtom: (command, context, style) => + new NewLineAtom(command, context, style), +}); + defineFunction('mathop', '{:auto}', { createAtom: ( name: string, diff --git a/src/core-definitions/symbols.ts b/src/core-definitions/symbols.ts index 3ad105003..ce94f1401 100644 --- a/src/core-definitions/symbols.ts +++ b/src/core-definitions/symbols.ts @@ -1,4 +1,3 @@ -import { NewLineAtom } from '../core-atoms/newline'; import { SpacingAtom } from '../core-atoms/spacing'; import { @@ -681,13 +680,6 @@ defineFunction(['!', ',', ':', ';', 'enskip', 'enspace', 'quad', 'qquad'], '', { new SpacingAtom(command, style, context), }); -// New line -newSymbols('\\\\', 'newline'); -defineFunction('\\', '', { - createAtom: (command, context, style) => - new NewLineAtom(command, context, style), -}); - // Punctuation newSymbols( [