-
+
}
+ />
- )
+ );
}
-export default RSEditButton;
\ No newline at end of file
+export default RSTokenButton;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/pages/RSFormPage/TablistTools.tsx b/rsconcept/frontend/src/pages/RSFormPage/TablistTools.tsx
index f95404bf..a98888ef 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/TablistTools.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/TablistTools.tsx
@@ -102,7 +102,7 @@ function TablistTools() {
}
dense
@@ -130,7 +130,7 @@ function TablistTools() {
:
diff --git a/rsconcept/frontend/src/pages/RSFormPage/textEditing.ts b/rsconcept/frontend/src/pages/RSFormPage/textEditing.ts
new file mode 100644
index 00000000..d8fb794a
--- /dev/null
+++ b/rsconcept/frontend/src/pages/RSFormPage/textEditing.ts
@@ -0,0 +1,212 @@
+// Formatted text editing helpers
+
+import { TokenID } from '../../utils/models'
+
+export function getSymbolSubstitute(input: string): string | undefined {
+ switch(input) {
+ case '`': return '∀';
+ case '~': return '∃';
+
+ // qwerty = μωερτπ
+ // asdfgh = ασδφγλ
+ // zxcvbn = ζξψθβη
+ case 'q': return 'μ';
+ case 'w': return 'ω';
+ case 'e': return 'ε';
+ case 'r': return 'ρ';
+ case 't': return 'τ';
+ case 'y': return 'π';
+
+ case 'a': return 'α';
+ case 's': return 'σ';
+ case 'd': return 'δ';
+ case 'f': return 'φ';
+ case 'g': return 'γ';
+ case 'h': return 'λ';
+
+ case 'z': return 'ζ';
+ case 'x': return 'ξ';
+ case 'c': return 'ψ';
+ case 'v': return 'θ';
+ case 'b': return 'β';
+ case 'n': return 'η';
+ }
+ return undefined;
+}
+
+export interface IManagedText {
+ value: string
+ selStart: number
+ selEnd: number
+}
+
+// Note: Wrapper class for textareafield.
+// WARNING! Manipulations on value do not support UNDO browser
+// WARNING! No checks for selection out of text boundaries
+export class TextWrapper implements IManagedText {
+ value: string
+ selStart: number
+ selEnd: number
+ object: HTMLTextAreaElement
+
+ constructor(element: HTMLTextAreaElement) {
+ this.object = element;
+ this.value = this.object.value;
+ this.selStart = this.object.selectionStart;
+ this.selEnd = this.object.selectionEnd;
+ }
+
+ focus() {
+ this.object.focus();
+ }
+
+ refresh() {
+ this.value = this.object.value;
+ this.selStart = this.object.selectionStart;
+ this.selEnd = this.object.selectionEnd;
+ }
+
+ finalize() {
+ this.object.value = this.value;
+ this.object.selectionStart = this.selStart;
+ this.object.selectionEnd = this.selEnd;
+ }
+
+ replaceWith(data: string) {
+ this.value = this.value.substring(0, this.selStart) + data + this.value.substring(this.selEnd, this.value.length);
+ this.selEnd += data.length - this.selEnd + this.selStart;
+ this.selStart = this.selEnd;
+ }
+
+ envelopeWith(left: string, right: string) {
+ this.value = this.value.substring(0, this.selStart) + left +
+ this.value.substring(this.selStart, this.selEnd) + right +
+ this.value.substring(this.selEnd, this.value.length);
+ this.selEnd += left.length + right.length;
+ }
+
+ moveSel(shift: number) {
+ this.selStart += shift;
+ this.selEnd += shift;
+ }
+
+ setSel(start: number, end: number) {
+ this.selStart = start;
+ this.selEnd = end;
+ }
+
+ insertChar(key: string) {
+ this.replaceWith(key);
+ }
+
+ insertToken(tokenID: TokenID): boolean {
+ switch(tokenID) {
+ case TokenID.NT_DECLARATIVE_EXPR: this.envelopeWith('D{ξ∈X1 | ', '}'); return true;
+ case TokenID.NT_IMPERATIVE_EXPR: this.envelopeWith('I{(σ, γ) | σ:∈X1; γ:=F1[σ]; ', '}'); return true;
+ case TokenID.NT_RECURSIVE_FULL: this.envelopeWith('R{ ξ:=D1 | 1=1 | ', '}'); return true;
+ case TokenID.BIGPR: this.envelopeWith('Pr1(', ')'); return true;
+ case TokenID.SMALLPR: this.envelopeWith('pr1(', ')'); return true;
+ case TokenID.FILTER: this.envelopeWith('Fi1[α](', ')'); return true;
+ case TokenID.REDUCE: this.envelopeWith('red(', ')'); return true;
+ case TokenID.CARD: this.envelopeWith('card(', ')'); return true;
+ case TokenID.BOOL: this.envelopeWith('bool(', ')'); return true;
+ case TokenID.DEBOOL: this.envelopeWith('debool(', ')'); return true;
+
+ case TokenID.PUNC_PL: {
+ this.envelopeWith('(', ')');
+ this.selEnd = this.selStart + 1;
+ this.selStart = this.selEnd;
+ return true;
+ }
+ case TokenID.PUNC_SL: {
+ this.envelopeWith('[', ']');
+ this.selEnd = this.selStart + 1;
+ this.selStart = this.selEnd;
+ return true;
+ }
+ case TokenID.BOOLEAN: {
+ if (this.selEnd !== this.selStart && this.value.at(this.selStart) === 'ℬ') {
+ this.envelopeWith('ℬ', '');
+ } else {
+ this.envelopeWith('ℬ(', ')');
+ }
+ return true;
+ }
+
+ case TokenID.DECART: this.replaceWith('×'); return true;
+ case TokenID.FORALL: this.replaceWith('∀'); return true;
+ case TokenID.EXISTS: this.replaceWith('∃'); return true;
+ case TokenID.IN: this.replaceWith('∈'); return true;
+ case TokenID.NOTIN: this.replaceWith('∉'); return true;
+ case TokenID.OR: this.replaceWith('∨'); return true;
+ case TokenID.AND: this.replaceWith('&'); return true;
+ case TokenID.SUBSET_OR_EQ: this.replaceWith('⊆'); return true;
+ case TokenID.IMPLICATION: this.replaceWith('⇒'); return true;
+ case TokenID.INTERSECTION: this.replaceWith('∩'); return true;
+ case TokenID.UNION: this.replaceWith('∪'); return true;
+ case TokenID.SET_MINUS: this.replaceWith('\\'); return true;
+ case TokenID.SYMMINUS: this.replaceWith('∆'); return true;
+ case TokenID.LIT_EMPTYSET: this.replaceWith('∅'); return true;
+ case TokenID.LIT_INTSET: this.replaceWith('Z'); return true;
+ case TokenID.SUBSET: this.replaceWith('⊂'); return true;
+ case TokenID.NOTSUBSET: this.replaceWith('⊄'); return true;
+ case TokenID.EQUAL: this.replaceWith('='); return true;
+ case TokenID.NOTEQUAL: this.replaceWith('≠'); return true;
+ case TokenID.NOT: this.replaceWith('¬'); return true;
+ case TokenID.EQUIVALENT: this.replaceWith('⇔'); return true;
+ case TokenID.GREATER_OR_EQ: this.replaceWith('≥'); return true;
+ case TokenID.LESSER_OR_EQ: this.replaceWith('≤'); return true;
+ case TokenID.PUNC_ASSIGN: this.replaceWith(':='); return true;
+ case TokenID.PUNC_ITERATE: this.replaceWith(':∈'); return true;
+ case TokenID.MULTIPLY: this.replaceWith('*'); return true;
+ }
+ return false;
+ }
+
+ processAltKey(key: string): boolean {
+ switch(key) {
+ // qwert
+ // asdfg
+ // zxcvb
+ case 'q': return this.insertToken(TokenID.BIGPR);
+ case 'w': return this.insertToken(TokenID.SMALLPR);
+ case 'e': return this.insertToken(TokenID.BOOLEAN);
+ case 'r': return this.insertToken(TokenID.REDUCE);
+ case 't': return this.insertToken(TokenID.NT_RECURSIVE_FULL);
+ case 'a': return this.insertToken(TokenID.INTERSECTION);
+ case 's': return this.insertToken(TokenID.UNION);
+ case 'd': return this.insertToken(TokenID.NT_DECLARATIVE_EXPR);
+ case 'f': return this.insertToken(TokenID.FILTER);
+ case 'g': return this.insertToken(TokenID.NT_IMPERATIVE_EXPR);
+ case 'z': return this.insertToken(TokenID.LIT_INTSET);
+ case 'x': return this.insertToken(TokenID.LIT_EMPTYSET);
+ case 'c': return this.insertToken(TokenID.CARD);
+ case 'v': return this.insertToken(TokenID.DEBOOL);
+ case 'b': return this.insertToken(TokenID.BOOL);
+
+ // `123456
+ // ~!@#$%^
+ case '`': return this.insertToken(TokenID.NOT);
+ case '~': return this.insertToken(TokenID.NOTEQUAL);
+ case '1': return this.insertToken(TokenID.IN);
+ case '!': return this.insertToken(TokenID.NOTIN); // Alt + 1
+ case '2': return this.insertToken(TokenID.SUBSET_OR_EQ);
+ case '@': return this.insertToken(TokenID.NOTSUBSET); // Alt + 2
+ case '3': return this.insertToken(TokenID.AND);
+ case '#': return this.insertToken(TokenID.OR); // Alt + 3
+ case '4': return this.insertToken(TokenID.IMPLICATION);
+ case '$': return this.insertToken(TokenID.EQUIVALENT); // Alt + 4
+ case '5': return this.insertToken(TokenID.SET_MINUS);
+ case '%': return this.insertToken(TokenID.SYMMINUS); // Alt + 5
+ case '6': return this.insertToken(TokenID.PUNC_ITERATE);
+ case '^': return this.insertToken(TokenID.PUNC_ASSIGN); // Alt + 6
+ case '7': return this.insertToken(TokenID.SUBSET);
+ case '&': return this.insertToken(TokenID.GREATER_OR_EQ); // Alt + 7
+ case '8': return this.insertToken(TokenID.MULTIPLY);
+ case '*': return this.insertToken(TokenID.LESSER_OR_EQ); // Alt + 8
+ case '(': return this.insertToken(TokenID.PUNC_PL); // Alt + 9
+ case '[': return this.insertToken(TokenID.PUNC_SL);
+ }
+ return false;
+ }
+};
\ No newline at end of file
diff --git a/rsconcept/frontend/src/utils/staticUI.ts b/rsconcept/frontend/src/utils/staticUI.ts
index 7481a965..966f9948 100644
--- a/rsconcept/frontend/src/utils/staticUI.ts
+++ b/rsconcept/frontend/src/utils/staticUI.ts
@@ -24,8 +24,8 @@ export function getTypeLabel(cst: IConstituenta) {
export function getRSButtonData(id: TokenID): IRSButtonData {
switch(id) {
case TokenID.BOOLEAN: return {
- text: 'ℬ',
- tooltip: 'Булеан [Shift + B]',
+ text: 'ℬ()',
+ tooltip: 'Булеан [Alt + E]',
};
case TokenID.DECART: return {
text: '×',
@@ -33,11 +33,11 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
};
case TokenID.PUNC_PL: return {
text: '( )',
- tooltip: 'Скобки вокруг выражения',
+ tooltip: 'Скобки вокруг выражения [ Alt + Shift + 9 ]',
};
case TokenID.PUNC_SL: return {
text: '[ ]',
- tooltip: 'Скобки вокруг выражения',
+ tooltip: 'Скобки вокруг выражения [ Alt + [ ]',
};
case TokenID.FORALL: return {
text: '∀',
@@ -49,31 +49,31 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
};
case TokenID.NOT: return {
text: '¬',
- tooltip: 'Отрицание [Alt + Shift + -]',
+ tooltip: 'Отрицание [Alt + `]',
};
case TokenID.AND: return {
text: '&',
- tooltip: 'Конъюнкция [Shift + 7]',
+ tooltip: 'Конъюнкция [Alt + 3 ~ Shift + 7]',
};
case TokenID.OR: return {
text: '∨',
- tooltip: 'дизъюнкция [Alt + Shift + 7]',
+ tooltip: 'дизъюнкция [Alt + Shift + 3]',
};
case TokenID.IMPLICATION: return {
text: '⇒',
- tooltip: 'импликация [Alt + Shift + ]]',
+ tooltip: 'импликация [Alt + 4]',
};
case TokenID.EQUIVALENT: return {
text: '⇔',
- tooltip: 'эквивалентность [Alt + Shift + []',
+ tooltip: 'эквивалентность [Alt + Shift + 4]',
};
case TokenID.LIT_EMPTYSET: return {
text: '∅',
- tooltip: 'пустое множество [Alt + Shift + 0]',
+ tooltip: 'пустое множество [Alt + X]',
};
case TokenID.LIT_INTSET: return {
text: 'Z',
- tooltip: 'целые числа',
+ tooltip: 'целые числа [Alt + Z]',
};
case TokenID.EQUAL: return {
text: '=',
@@ -81,7 +81,7 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
};
case TokenID.NOTEQUAL: return {
text: '≠',
- tooltip: 'неравенство [Alt + Shift + =]',
+ tooltip: 'неравенство [Alt + Shift + `]',
};
case TokenID.GREATER_OR_EQ: return {
text: '≥',
@@ -93,7 +93,7 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
};
case TokenID.IN: return {
text: '∈',
- tooltip: 'принадлежит [Alt + \']',
+ tooltip: 'быть элементом (принадлежит) [Alt + \']',
};
case TokenID.NOTIN: return {
text: '∉',
@@ -101,7 +101,7 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
};
case TokenID.SUBSET_OR_EQ: return {
text: '⊆',
- tooltip: 'быть частью (нестрогое подмножество) [Alt + Shift + L]',
+ tooltip: 'быть частью (нестрогое подмножество) [Alt + 2]',
};
case TokenID.SUBSET: return {
text: '⊂',
@@ -109,7 +109,7 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
};
case TokenID.NOTSUBSET: return {
text: '⊄',
- tooltip: 'не подмножество [Alt + Shift + ;]',
+ tooltip: 'не подмножество [Alt + Shift + 2]',
};
case TokenID.INTERSECTION: return {
text: '∩',
@@ -119,36 +119,36 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
text: '∪',
tooltip: 'объединение [Alt + U]',
};
- case TokenID.MINUS: return {
+ case TokenID.SET_MINUS: return {
text: '\\',
- tooltip: 'Разность множеств',
+ tooltip: 'Разность множеств [Alt + 5]',
};
case TokenID.SYMMINUS: return {
text: '∆',
- tooltip: 'Симметрическая разность',
+ tooltip: 'Симметрическая разность [Alt + Shift + 5]',
};
case TokenID.NT_DECLARATIVE_EXPR: return {
text: 'D{}',
- tooltip: 'Декларативная форма определения терма',
+ tooltip: 'Декларативная форма определения терма [Alt + D]',
};
case TokenID.NT_IMPERATIVE_EXPR: return {
text: 'I{}',
- tooltip: 'императивная форма определения терма',
+ tooltip: 'императивная форма определения терма [Alt + G]',
};
case TokenID.NT_RECURSIVE_FULL: return {
text: 'R{}',
- tooltip: 'рекурсивная (цикличная) форма определения терма',
+ tooltip: 'рекурсивная (цикличная) форма определения терма [Alt + T]',
};
case TokenID.BIGPR: return {
text: 'Pr1()',
- tooltip: 'большая проекция [Alt + W]',
+ tooltip: 'большая проекция [Alt + Q]',
};
case TokenID.SMALLPR: return {
text: 'pr1()',
- tooltip: 'малая проекция [Alt + Q]',
+ tooltip: 'малая проекция [Alt + W]',
};
case TokenID.FILTER: return {
- text: 'Fi[]()',
+ text: 'Fi1[]()',
tooltip: 'фильтр [Alt + F]',
};
case TokenID.REDUCE: return {
@@ -165,7 +165,7 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
};
case TokenID.DEBOOL: return {
text: 'debool()',
- tooltip: 'десинглетон [Alt + D]',
+ tooltip: 'десинглетон [Alt + V]',
};
case TokenID.PUNC_ASSIGN: return {
text: ':=',
@@ -248,6 +248,6 @@ export function getStatusInfo(status?: ExpressionStatus): IStatusInfo {
};
}
-export function extractGlobals(expression: string) {
- return expression.match(/[XCSADFPT]\d+/g) || [];
+export function extractGlobals(expression: string): Set {
+ return new Set(expression.match(/[XCSADFPT]\d+/g) || []);
}
\ No newline at end of file
diff --git a/rsconcept/frontend/src/utils/textEditing.ts b/rsconcept/frontend/src/utils/textEditing.ts
deleted file mode 100644
index e8c9df22..00000000
--- a/rsconcept/frontend/src/utils/textEditing.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-// Formatted text editing helpers
-
-import { TokenID } from './models'
-
-export interface IManagedText {
- value: string
- selStart: number
- selEnd: number
-}
-
-// Note: Wrapper class for textareafield.
-// WARNING! Manipulations on value do not support UNDO browser
-// WARNING! No checks for selection out of text boundaries
-export class TextWrapper implements IManagedText {
- value: string
- selStart: number
- selEnd: number
- object: HTMLTextAreaElement
-
- constructor(element: HTMLTextAreaElement) {
- this.object = element;
- this.value = this.object.value;
- this.selStart = this.object.selectionStart;
- this.selEnd = this.object.selectionEnd;
- }
-
- focus() {
- this.object.focus();
- }
-
- refresh() {
- this.value = this.object.value;
- this.selStart = this.object.selectionStart;
- this.selEnd = this.object.selectionEnd;
- }
-
- finalize() {
- this.object.value = this.value;
- this.object.selectionStart = this.selStart;
- this.object.selectionEnd = this.selEnd;
- }
-
- replaceWith(data: string) {
- this.value = this.value.substring(0, this.selStart) + data + this.value.substring(this.selEnd, this.value.length);
- this.selEnd += data.length - this.selEnd + this.selStart;
- this.selStart = this.selEnd;
- }
-
- envelopeWith(left: string, right: string) {
- this.value = this.value.substring(0, this.selStart) + left +
- this.value.substring(this.selStart, this.selEnd) + right +
- this.value.substring(this.selEnd, this.value.length);
- this.selEnd += left.length + right.length;
- }
-
- moveSel(shift: number) {
- this.selStart += shift;
- this.selEnd += shift;
- }
-
- setSel(start: number, end: number) {
- this.selStart = start;
- this.selEnd = end;
- }
-
- insertToken(tokenID: TokenID) {
- switch(tokenID) {
- case TokenID.NT_DECLARATIVE_EXPR: this.envelopeWith('D{ξ∈X1 | ', '}'); return;
- case TokenID.NT_IMPERATIVE_EXPR: this.envelopeWith('I{(σ, γ) | σ:∈X1; γ:=F1[σ]; ', '}'); return;
- case TokenID.NT_RECURSIVE_FULL: this.envelopeWith('R{ ξ:=D1 | 1=1 | ', '}'); return;
- case TokenID.BIGPR: this.envelopeWith('Pr1(', ')'); return;
- case TokenID.SMALLPR: this.envelopeWith('pr1(', ')'); return;
- case TokenID.FILTER: this.envelopeWith('Fi1[α](', ')'); return;
- case TokenID.REDUCE: this.envelopeWith('red(', ')'); return;
- case TokenID.CARD: this.envelopeWith('card(', ')'); return;
- case TokenID.BOOL: this.envelopeWith('bool(', ')'); return;
- case TokenID.DEBOOL: this.envelopeWith('debool(', ')'); return;
- case TokenID.PUNC_PL: this.envelopeWith('(', ')'); return;
- case TokenID.PUNC_SL: this.envelopeWith('[', ']'); return;
-
- case TokenID.BOOLEAN: {
- if (this.selEnd !== this.selStart && this.value.at(this.selStart) === 'ℬ') {
- this.envelopeWith('ℬ', '');
- } else {
- this.envelopeWith('ℬ(', ')');
- }
- return;
- }
-
- case TokenID.DECART: this.replaceWith('×'); return;
- case TokenID.FORALL: this.replaceWith('∀'); return;
- case TokenID.EXISTS: this.replaceWith('∃'); return;
- case TokenID.IN: this.replaceWith('∈'); return;
- case TokenID.NOTIN: this.replaceWith('∉'); return;
- case TokenID.OR: this.replaceWith('∨'); return;
- case TokenID.AND: this.replaceWith('&'); return;
- case TokenID.SUBSET_OR_EQ: this.replaceWith('⊆'); return;
- case TokenID.IMPLICATION: this.replaceWith('⇒'); return;
- case TokenID.INTERSECTION: this.replaceWith('∩'); return;
- case TokenID.UNION: this.replaceWith('∪'); return;
- case TokenID.MINUS: this.replaceWith('\\'); return;
- case TokenID.SYMMINUS: this.replaceWith('∆'); return;
- case TokenID.LIT_EMPTYSET: this.replaceWith('∅'); return;
- case TokenID.LIT_INTSET: this.replaceWith('Z'); return;
- case TokenID.SUBSET: this.replaceWith('⊂'); return;
- case TokenID.NOTSUBSET: this.replaceWith('⊄'); return;
- case TokenID.EQUAL: this.replaceWith('='); return;
- case TokenID.NOTEQUAL: this.replaceWith('≠'); return;
- case TokenID.NOT: this.replaceWith('¬'); return;
- case TokenID.EQUIVALENT: this.replaceWith('⇔'); return;
- case TokenID.GREATER_OR_EQ: this.replaceWith('≥'); return;
- case TokenID.LESSER_OR_EQ: this.replaceWith('≤'); return;
- case TokenID.PUNC_ASSIGN: this.replaceWith(':='); return;
- case TokenID.PUNC_ITERATE: this.replaceWith(':∈'); return;
-
- }
- }
-};
\ No newline at end of file