mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 21:10:38 +03:00
Display text reference tooltips
This commit is contained in:
parent
1f8f904626
commit
78c6a2306e
|
@ -13,7 +13,7 @@ import Label from '../Common/Label';
|
||||||
import { ccBracketMatching } from './bracketMatching';
|
import { ccBracketMatching } from './bracketMatching';
|
||||||
import { RSLanguage } from './rslang';
|
import { RSLanguage } from './rslang';
|
||||||
import { getSymbolSubstitute,TextWrapper } from './textEditing';
|
import { getSymbolSubstitute,TextWrapper } from './textEditing';
|
||||||
import { rshoverTooltip as rsHoverTooltip } from './tooltip';
|
import { rsHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
const editorSetup: BasicSetupOptions = {
|
||||||
highlightSpecialChars: false,
|
highlightSpecialChars: false,
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import {LRLanguage} from '@codemirror/language'
|
import {LRLanguage} from '@codemirror/language'
|
||||||
|
|
||||||
import { parser } from './parser';
|
import { parser } from './parser';
|
||||||
|
import { Function, Global, Predicate } from './parser.terms';
|
||||||
|
|
||||||
|
export const GlobalTokens: number[] = [
|
||||||
|
Global, Function, Predicate
|
||||||
|
]
|
||||||
|
|
||||||
export const RSLanguage = LRLanguage.define({
|
export const RSLanguage = LRLanguage.define({
|
||||||
parser: parser,
|
parser: parser,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { printTree } from '../../../utils/print-lezer-tree';
|
import { printTree } from '../../../utils/codemirror';
|
||||||
import { parser } from './parser';
|
import { parser } from './parser';
|
||||||
|
|
||||||
const testData = [
|
const testData = [
|
||||||
|
|
|
@ -1,50 +1,33 @@
|
||||||
|
import { syntaxTree } from "@codemirror/language"
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { hoverTooltip } from '@codemirror/view';
|
import { hoverTooltip } from '@codemirror/view';
|
||||||
|
import { EditorState } from '@uiw/react-codemirror';
|
||||||
|
|
||||||
import { IConstituenta } from '../../models/rsform';
|
import { IConstituenta } from '../../models/rsform';
|
||||||
import { labelCstTypification } from '../../utils/labels';
|
import { findEnvelopingNodes } from '../../utils/codemirror';
|
||||||
|
import { domTooltipConstituenta } from '../../utils/codemirror';
|
||||||
|
import { GlobalTokens } from './rslang';
|
||||||
|
|
||||||
function createTooltipFor(cst: IConstituenta) {
|
function findAliasAt(pos: number, state: EditorState) {
|
||||||
const dom = document.createElement('div');
|
const { from: lineStart, to: lineEnd, text } = state.doc.lineAt(pos);
|
||||||
dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-tooltip text-sm px-2 py-2';
|
const nodes = findEnvelopingNodes(pos, pos, syntaxTree(state), GlobalTokens);
|
||||||
const alias = document.createElement('p');
|
let alias = '';
|
||||||
alias.innerHTML = `<b>${cst.alias}:</b> ${labelCstTypification(cst)}`;
|
let start = 0;
|
||||||
dom.appendChild(alias);
|
let end = 0;
|
||||||
if (cst.term_resolved) {
|
nodes.forEach(
|
||||||
const term = document.createElement('p');
|
node => {
|
||||||
term.innerHTML = `<b>Термин:</b> ${cst.term_resolved}`;
|
if (node.to <= lineEnd && node.from >= lineStart) {
|
||||||
dom.appendChild(term);
|
alias = text.slice(node.from - lineStart, node.to - lineStart);
|
||||||
|
start = node.from;
|
||||||
|
end = node.to;
|
||||||
}
|
}
|
||||||
if (cst.definition_formal) {
|
});
|
||||||
const expression = document.createElement('p');
|
return {alias, start, end};
|
||||||
expression.innerHTML = `<b>Выражение:</b> ${cst.definition_formal}`;
|
|
||||||
dom.appendChild(expression);
|
|
||||||
}
|
|
||||||
if (cst.definition_resolved) {
|
|
||||||
const definition = document.createElement('p');
|
|
||||||
definition.innerHTML = `<b>Определение:</b> ${cst.definition_resolved}`;
|
|
||||||
dom.appendChild(definition);
|
|
||||||
}
|
|
||||||
if (cst.convention) {
|
|
||||||
const convention = document.createElement('p');
|
|
||||||
convention.innerHTML = `<b>Конвенция:</b> ${cst.convention}`;
|
|
||||||
dom.appendChild(convention);
|
|
||||||
}
|
|
||||||
return { dom: dom }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getHoverTooltip = (items: IConstituenta[]) => {
|
const globalsHoverTooltip = (items: IConstituenta[]) => {
|
||||||
return hoverTooltip((view, pos, side) => {
|
return hoverTooltip((view, pos) => {
|
||||||
const {from, to, text} = view.state.doc.lineAt(pos);
|
const { alias, start, end } = findAliasAt(pos, view.state);
|
||||||
let start = pos, end = pos;
|
|
||||||
while (start > from && /\w/.test(text[start - from - 1]))
|
|
||||||
start--;
|
|
||||||
while (end < to && /\w/.test(text[end - from]))
|
|
||||||
end++;
|
|
||||||
if (start === pos && side < 0 || end === pos && side > 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const alias = text.slice(start - from, end - from);
|
|
||||||
const cst = items.find(cst => cst.alias === alias);
|
const cst = items.find(cst => cst.alias === alias);
|
||||||
if (!cst) {
|
if (!cst) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -53,11 +36,11 @@ export const getHoverTooltip = (items: IConstituenta[]) => {
|
||||||
pos: start,
|
pos: start,
|
||||||
end: end,
|
end: end,
|
||||||
above: false,
|
above: false,
|
||||||
create: () => createTooltipFor(cst)
|
create: () => domTooltipConstituenta(cst)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rshoverTooltip(items: IConstituenta[]): Extension {
|
export function rsHoverTooltip(items: IConstituenta[]): Extension {
|
||||||
return [getHoverTooltip(items)];
|
return [globalsHoverTooltip(items)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import Label from '../Common/Label';
|
||||||
import Modal from '../Common/Modal';
|
import Modal from '../Common/Modal';
|
||||||
import PrettyJson from '../Common/PrettyJSON';
|
import PrettyJson from '../Common/PrettyJSON';
|
||||||
import { NaturalLanguage } from './parse';
|
import { NaturalLanguage } from './parse';
|
||||||
import { rshoverTooltip as rsHoverTooltip } from './tooltip';
|
import { refsHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
const editorSetup: BasicSetupOptions = {
|
||||||
highlightSpecialChars: false,
|
highlightSpecialChars: false,
|
||||||
|
@ -87,7 +87,7 @@ function RefsInput({
|
||||||
selection: colors.bgHover
|
selection: colors.bgHover
|
||||||
},
|
},
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: tags.name, color: colors.fgPurple }, // GlobalID
|
{ tag: tags.name, color: colors.fgPurple, cursor: 'pointer' }, // GlobalID
|
||||||
{ tag: tags.literal, color: colors.fgTeal }, // literals
|
{ tag: tags.literal, color: colors.fgTeal }, // literals
|
||||||
]
|
]
|
||||||
}), [editable, colors, darkMode]);
|
}), [editable, colors, darkMode]);
|
||||||
|
@ -96,8 +96,8 @@ function RefsInput({
|
||||||
() => [
|
() => [
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
NaturalLanguage,
|
NaturalLanguage,
|
||||||
rsHoverTooltip(schema?.items || []),
|
refsHoverTooltip(schema?.items || [], colors),
|
||||||
], [schema?.items]);
|
], [schema?.items, colors]);
|
||||||
|
|
||||||
function handleChange(newValue: string) {
|
function handleChange(newValue: string) {
|
||||||
if (onChange) onChange(newValue);
|
if (onChange) onChange(newValue);
|
||||||
|
|
|
@ -3,8 +3,9 @@ import {styleTags, tags} from '@lezer/highlight';
|
||||||
export const highlighting = styleTags({
|
export const highlighting = styleTags({
|
||||||
RefEntity: tags.name,
|
RefEntity: tags.name,
|
||||||
Global: tags.name,
|
Global: tags.name,
|
||||||
Gram: tags.name,
|
Grams: tags.name,
|
||||||
|
|
||||||
RefSyntactic: tags.literal,
|
RefSyntactic: tags.literal,
|
||||||
Offset: tags.literal,
|
Offset: tags.literal,
|
||||||
|
Nominal: tags.literal,
|
||||||
});
|
});
|
|
@ -3,8 +3,8 @@ export const
|
||||||
Text = 1,
|
Text = 1,
|
||||||
RefEntity = 2,
|
RefEntity = 2,
|
||||||
Global = 3,
|
Global = 3,
|
||||||
Gram = 4,
|
Grams = 4,
|
||||||
RefSyntactic = 5,
|
RefSyntactic = 5,
|
||||||
Offset = 6,
|
Offset = 6,
|
||||||
Nominal = 7,
|
Nominal = 7,
|
||||||
Word = 8
|
Filler = 8
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import { printTree } from '../../../utils/print-lezer-tree';
|
import { printTree } from '../../../utils/codemirror';
|
||||||
import { parser } from './parser';
|
import { parser } from './parser';
|
||||||
|
|
||||||
const testData = [
|
const testData = [
|
||||||
['', '[Text]'],
|
['', '[Text]'],
|
||||||
['тест русский', '[Text[Word][Word]]'],
|
['тест русский', '[Text[Filler]]'],
|
||||||
['test english', '[Text[Word][Word]]'],
|
['test english', '[Text[Filler]]'],
|
||||||
['test greek σσσ', '[Text[Word][Word][Word]]'],
|
['test greek σσσ', '[Text[Filler]]'],
|
||||||
['X1 раз два X2', '[Text[Word][Word][Word][Word]]'],
|
['X1 раз два X2', '[Text[Filler]]'],
|
||||||
|
|
||||||
['@{1| черный }', '[Text[RefSyntactic[Offset][Nominal[Word]]]]'],
|
['@{1| черный }', '[Text[RefSyntactic[Offset][Nominal]]]'],
|
||||||
['@{-1| черный }', '[Text[RefSyntactic[Offset][Nominal[Word]]]]'],
|
['@{-1| черный }', '[Text[RefSyntactic[Offset][Nominal]]]'],
|
||||||
['@{-100| черный слон }', '[Text[RefSyntactic[Offset][Nominal[Word][Word]]]]'],
|
['@{-100| черный слон }', '[Text[RefSyntactic[Offset][Nominal]]]'],
|
||||||
['@{X1|VERB,past,sing}', '[Text[RefEntity[Global][Gram][Gram][Gram]]]'],
|
['@{X1|VERB,past,sing}', '[Text[RefEntity[Global][Grams]]]'],
|
||||||
['@{X12|VERB,past,sing}', '[Text[RefEntity[Global][Gram][Gram][Gram]]]'],
|
['@{X12|VERB,past,sing}', '[Text[RefEntity[Global][Grams]]]'],
|
||||||
];
|
];
|
||||||
|
|
||||||
describe('Testing NaturalParser', () => {
|
describe('Testing NaturalParser', () => {
|
||||||
|
|
|
@ -3,16 +3,16 @@ import {LRParser} from "@lezer/lr"
|
||||||
import {highlighting} from "./highlight.ts"
|
import {highlighting} from "./highlight.ts"
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: "#rQVQPOOO_QQO'#C^OOQO'#Ck'#CkOOQO'#Cj'#CjOOQO'#Ce'#CeQVQPOOOgQPO,58xOlQPO,58{OOQO-E6c-E6cOqQSO1G.dOvQPO1G.gO{QQO'#CoO!TQPO7+$OOOQO'#Cf'#CfO!YQPO'#CcO!bQPO7+$ROqQSO,59ZOOQO<<Gj<<GjOOQO-E6d-E6dOOQO<<Gm<<GmOOQO1G.u1G.u",
|
states: "$[QVQPOOO_QQO'#C^OOQO'#Ck'#CkOgQPO'#CsOOQO'#Cd'#CdOOQO'#Cj'#CjOOQO'#Ce'#CeQVQPOOOrQPO,58xOwQPO,58{OOQO,59_,59_OOQO-E6c-E6cO|QSO1G.dO!RQPO1G.gO!WQQO'#CoOOQO'#C`'#C`O!`QPO7+$OOOQO'#Cf'#CfO!eQPO'#CcO!mQPO7+$RO|QSO,59ZOOQO<<Gj<<GjOOQO-E6d-E6dOOQO<<Gm<<GmOOQO1G.u1G.u",
|
||||||
stateData: "!j~O]OS~OWROaPO~ORUOUVO~ObXO~ObYO~OSZO~OW]O~Od`O`cX~O`aO~OW]O`VX~O`cO~OW]~",
|
stateData: "!u~O]OS~OaPOfRO~ORWOUXO~OfROZgXagX~Ob[O~Ob]O~Od^O~OfaO~OedO`cX~O`eO~OfaO`VX~O`gO~Of]~",
|
||||||
goto: "!WdPPePPePiPlrPPPx|PPP!QTQOTR_YQTORWTQ^YRb^TSOTTROTQ[XRd`",
|
goto: "!fhPPiPmiPpsw}PPP!TsPPP!XPPP!_TQOVR`[Rc]TTOVQVORZVQb]RfbTUOVQ_[RhdSSOVRYR",
|
||||||
nodeNames: "⚠ Text RefEntity Global Gram RefSyntactic Offset Nominal Word",
|
nodeNames: "⚠ Text RefEntity Global Grams RefSyntactic Offset Nominal Filler",
|
||||||
maxTerm: 20,
|
maxTerm: 23,
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 2,
|
repeatNodeCount: 2,
|
||||||
tokenData: "-}~R!TOX$bXZ%SZ^%w^p$bpq%Sq|$b|}'p}!O(^!O!Q$b!Q!R)a!R![*a![!b$b!b!c+c!c!d+n!d!e)a!e!f+n!f!g+n!g!h)a!h!i+n!i!r)a!r!s+n!s!t)a!t!u+n!u!v+n!v!w+n!w!z)a!z!{+n!{!})a!}#T$b#T#o)a#p#q-s#q#r-x#r#y$b#y#z%w#z$f$b$f$g%w$g#BY$b#BY#BZ%w#BZ$IS$b$IS$I_%w$I_$I|$b$I|$JO%w$JO$JT$b$JT$JU%w$JU$KV$b$KV$KW%w$KW&FU$b&FU&FV%w&FV;'S$b;'S;=`$|<%lO$bP$gVWPOX$bZp$bq!b$b!c#o$b#r;'S$b;'S;=`$|<%lO$bP%PP;=`<%l$b~%XY]~X^%Spq%S#y#z%S$f$g%S#BY#BZ%S$IS$I_%S$I|$JO%S$JT$JU%S$KV$KW%S&FU&FV%S~&OjWP]~OX$bXZ%SZ^%w^p$bpq%Sq!b$b!c#o$b#r#y$b#y#z%w#z$f$b$f$g%w$g#BY$b#BY#BZ%w#BZ$IS$b$IS$I_%w$I_$I|$b$I|$JO%w$JO$JT$b$JT$JU%w$JU$KV$b$KV$KW%w$KW&FU$b&FU&FV%w&FV;'S$b;'S;=`$|<%lO$bR'wVdQWPOX$bZp$bq!b$b!c#o$b#r;'S$b;'S;=`$|<%lO$bV(e^SSWPOX$bZp$bq}$b}!O)a!O!Q$b!Q!R)a!R![*a![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$bT)h]SSWPOX$bZp$bq}$b}!O)a!O!Q$b!Q![)a![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$bV*j]SSUQWPOX$bZp$bq}$b}!O)a!O!Q$b!Q![*a![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$b~+fP#o#p+i~+nOa~V+u^SSWPOX$bZp$bq}$b}!O)a!O!Q$b!Q!R)a!R![,q![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$bV,z]RQSSWPOX$bZp$bq}$b}!O)a!O!Q$b!Q![,q![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$b~-xOb~~-}O`~",
|
tokenData: "-}~R!TOX$bXZ%SZ^%w^p$bpq%Sq|$b|}'p}!O(^!O!Q$b!Q!R)a!R![*a![!b$b!b!c+c!c!d+n!d!e)a!e!f+n!f!g+n!g!h)a!h!i+n!i!r)a!r!s+n!s!t)a!t!u+n!u!v+n!v!w+n!w!z)a!z!{+n!{!})a!}#T$b#T#o)a#p#q-s#q#r-x#r#y$b#y#z%w#z$f$b$f$g%w$g#BY$b#BY#BZ%w#BZ$IS$b$IS$I_%w$I_$I|$b$I|$JO%w$JO$JT$b$JT$JU%w$JU$KV$b$KV$KW%w$KW&FU$b&FU&FV%w&FV;'S$b;'S;=`$|<%lO$bP$gVfPOX$bZp$bq!b$b!c#o$b#r;'S$b;'S;=`$|<%lO$bP%PP;=`<%l$b~%XY]~X^%Spq%S#y#z%S$f$g%S#BY#BZ%S$IS$I_%S$I|$JO%S$JT$JU%S$KV$KW%S&FU&FV%S~&OjfP]~OX$bXZ%SZ^%w^p$bpq%Sq!b$b!c#o$b#r#y$b#y#z%w#z$f$b$f$g%w$g#BY$b#BY#BZ%w#BZ$IS$b$IS$I_%w$I_$I|$b$I|$JO%w$JO$JT$b$JT$JU%w$JU$KV$b$KV$KW%w$KW&FU$b&FU&FV%w&FV;'S$b;'S;=`$|<%lO$bR'wVeQfPOX$bZp$bq!b$b!c#o$b#r;'S$b;'S;=`$|<%lO$bV(e^dSfPOX$bZp$bq}$b}!O)a!O!Q$b!Q!R)a!R![*a![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$bT)h]dSfPOX$bZp$bq}$b}!O)a!O!Q$b!Q![)a![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$bV*j]UQdSfPOX$bZp$bq}$b}!O)a!O!Q$b!Q![*a![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$b~+fP#o#p+i~+nOa~V+u^dSfPOX$bZp$bq}$b}!O)a!O!Q$b!Q!R)a!R![,q![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$bV,z]RQdSfPOX$bZp$bq}$b}!O)a!O!Q$b!Q![,q![!b$b!c!})a!}#T$b#T#o)a#r;'S$b;'S;=`$|<%lO$b~-xOb~~-}O`~",
|
||||||
tokenizers: [0, 1, 2],
|
tokenizers: [0, 1, 2],
|
||||||
topRules: {"Text":[0,1]},
|
topRules: {"Text":[0,1]},
|
||||||
tokenPrec: 69
|
tokenPrec: 80
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@precedence {
|
@precedence {
|
||||||
|
text @right
|
||||||
p1
|
p1
|
||||||
p2
|
p2
|
||||||
}
|
}
|
||||||
|
@ -13,16 +14,22 @@
|
||||||
Offset { $[-]?$[1-9]$[0-9]* }
|
Offset { $[-]?$[1-9]$[0-9]* }
|
||||||
Global { $[XCSDATFPR]$[1-9]$[0-9]* }
|
Global { $[XCSDATFPR]$[1-9]$[0-9]* }
|
||||||
|
|
||||||
Word { ![@{|} \t\n]+ }
|
word { ![@{|} \t\n]+ }
|
||||||
|
|
||||||
Gram { $[-a-zA-Z0-9]+ }
|
gram { $[-a-zA-Z0-9]+ }
|
||||||
|
|
||||||
@precedence { Word, space }
|
@precedence { word, space }
|
||||||
}
|
}
|
||||||
|
|
||||||
textItem {
|
textItem {
|
||||||
!p1 ref |
|
!p1 ref |
|
||||||
!p2 Word
|
!p2 Filler
|
||||||
|
}
|
||||||
|
|
||||||
|
Filler { word_enum }
|
||||||
|
word_enum {
|
||||||
|
word |
|
||||||
|
word !text word_enum
|
||||||
}
|
}
|
||||||
|
|
||||||
ref {
|
ref {
|
||||||
|
@ -31,19 +38,18 @@ ref {
|
||||||
}
|
}
|
||||||
|
|
||||||
RefEntity {
|
RefEntity {
|
||||||
"@{" Global "|" grams "}"
|
"@{" Global "|" Grams "}"
|
||||||
|
}
|
||||||
|
Grams { gram_enum }
|
||||||
|
gram_enum {
|
||||||
|
gram |
|
||||||
|
gram "," gram_enum
|
||||||
}
|
}
|
||||||
|
|
||||||
RefSyntactic {
|
RefSyntactic {
|
||||||
"@{" Offset "|" Nominal "}"
|
"@{" Offset "|" Nominal "}"
|
||||||
}
|
}
|
||||||
|
Nominal { word+ }
|
||||||
Nominal { Word+ }
|
|
||||||
|
|
||||||
grams {
|
|
||||||
Gram |
|
|
||||||
Gram "," grams
|
|
||||||
}
|
|
||||||
|
|
||||||
@detectDelim
|
@detectDelim
|
||||||
|
|
||||||
|
|
|
@ -1,63 +1,43 @@
|
||||||
|
import { syntaxTree } from "@codemirror/language"
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { hoverTooltip } from '@codemirror/view';
|
import { hoverTooltip } from '@codemirror/view';
|
||||||
|
|
||||||
|
import { parseEntityReference, parseSyntacticReference } from '../../models/language';
|
||||||
import { IConstituenta } from '../../models/rsform';
|
import { IConstituenta } from '../../models/rsform';
|
||||||
import { labelCstTypification } from '../../utils/labels';
|
import { domTooltipEntityReference, domTooltipSyntacticReference, findEnvelopingNodes } from '../../utils/codemirror';
|
||||||
|
import { IColorTheme } from '../../utils/color';
|
||||||
|
import { RefEntity, RefSyntactic } from './parse/parser.terms';
|
||||||
|
|
||||||
function createTooltipFor(cst: IConstituenta) {
|
export const globalsHoverTooltip = (items: IConstituenta[], colors: IColorTheme) => {
|
||||||
const dom = document.createElement('div');
|
return hoverTooltip((view, pos) => {
|
||||||
dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-tooltip text-sm px-2 py-2';
|
const nodes = findEnvelopingNodes(pos, pos, syntaxTree(view.state), [RefEntity, RefSyntactic]);
|
||||||
const alias = document.createElement('p');
|
if (nodes.length !== 1) {
|
||||||
alias.innerHTML = `<b>${cst.alias}:</b> ${labelCstTypification(cst)}`;
|
|
||||||
dom.appendChild(alias);
|
|
||||||
if (cst.term_resolved) {
|
|
||||||
const term = document.createElement('p');
|
|
||||||
term.innerHTML = `<b>Термин:</b> ${cst.term_resolved}`;
|
|
||||||
dom.appendChild(term);
|
|
||||||
}
|
|
||||||
if (cst.definition_formal) {
|
|
||||||
const expression = document.createElement('p');
|
|
||||||
expression.innerHTML = `<b>Выражение:</b> ${cst.definition_formal}`;
|
|
||||||
dom.appendChild(expression);
|
|
||||||
}
|
|
||||||
if (cst.definition_resolved) {
|
|
||||||
const definition = document.createElement('p');
|
|
||||||
definition.innerHTML = `<b>Определение:</b> ${cst.definition_resolved}`;
|
|
||||||
dom.appendChild(definition);
|
|
||||||
}
|
|
||||||
if (cst.convention) {
|
|
||||||
const convention = document.createElement('p');
|
|
||||||
convention.innerHTML = `<b>Конвенция:</b> ${cst.convention}`;
|
|
||||||
dom.appendChild(convention);
|
|
||||||
}
|
|
||||||
return { dom: dom }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getHoverTooltip = (items: IConstituenta[]) => {
|
|
||||||
return hoverTooltip((view, pos, side) => {
|
|
||||||
const {from, to, text} = view.state.doc.lineAt(pos);
|
|
||||||
let start = pos, end = pos;
|
|
||||||
while (start > from && /\w/.test(text[start - from - 1]))
|
|
||||||
start--;
|
|
||||||
while (end < to && /\w/.test(text[end - from]))
|
|
||||||
end++;
|
|
||||||
if (start === pos && side < 0 || end === pos && side > 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const alias = text.slice(start - from, end - from);
|
|
||||||
const cst = items.find(cst => cst.alias === alias);
|
|
||||||
if (!cst) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const start = nodes[0].from;
|
||||||
|
const end = nodes[0].to;
|
||||||
|
const text = view.state.doc.sliceString(start, end);
|
||||||
|
if (nodes[0].type.id === RefEntity) {
|
||||||
|
const ref = parseEntityReference(text);
|
||||||
|
const cst = items.find(cst => cst.alias === ref.entity);
|
||||||
return {
|
return {
|
||||||
pos: start,
|
pos: start,
|
||||||
end: end,
|
end: end,
|
||||||
above: false,
|
above: false,
|
||||||
create: () => createTooltipFor(cst)
|
create: () => domTooltipEntityReference(ref, cst, colors)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const ref = parseSyntacticReference(text);
|
||||||
|
return {
|
||||||
|
pos: start,
|
||||||
|
end: end,
|
||||||
|
above: false,
|
||||||
|
create: () => domTooltipSyntacticReference(ref)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rshoverTooltip(items: IConstituenta[]): Extension {
|
export function refsHoverTooltip(items: IConstituenta[], colors: IColorTheme): Extension {
|
||||||
return [getHoverTooltip(items)];
|
return [globalsHoverTooltip(items, colors)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Grammeme, parseGrammemes } from './language';
|
import { Grammeme, parseEntityReference, parseGrammemes, parseSyntacticReference } from './language';
|
||||||
|
|
||||||
|
|
||||||
describe('Testing grammeme parsing', () => {
|
describe('Testing grammeme parsing', () => {
|
||||||
|
@ -11,10 +11,28 @@ describe('Testing grammeme parsing', () => {
|
||||||
|
|
||||||
test('regular grammemes',
|
test('regular grammemes',
|
||||||
() => {
|
() => {
|
||||||
expect(parseGrammemes('NOUN')).toStrictEqual([{type: Grammeme.NOUN, data: 'NOUN'}]);
|
expect(parseGrammemes('NOUN')).toStrictEqual([Grammeme.NOUN]);
|
||||||
expect(parseGrammemes('sing,nomn')).toStrictEqual([
|
expect(parseGrammemes('sing,nomn')).toStrictEqual([Grammeme.sing, Grammeme.nomn]);
|
||||||
{type: Grammeme.sing, data: 'sing'},
|
expect(parseGrammemes('nomn,sing')).toStrictEqual([Grammeme.sing, Grammeme.nomn]);
|
||||||
{type: Grammeme.nomn, data: 'nomn'}
|
expect(parseGrammemes('nomn,invalid,sing')).toStrictEqual([Grammeme.sing, Grammeme.nomn, 'invalid']);
|
||||||
]);
|
expect(parseGrammemes('invalid,test')).toStrictEqual(['invalid', 'test']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('Testing reference parsing', () => {
|
||||||
|
test('entity reference',
|
||||||
|
() => {
|
||||||
|
expect(parseEntityReference('@{ X1 | NOUN,sing }')).toStrictEqual({entity: 'X1', form: 'NOUN,sing'});
|
||||||
|
expect(parseEntityReference('@{X1|NOUN,sing}')).toStrictEqual({entity: 'X1', form: 'NOUN,sing'});
|
||||||
|
expect(parseEntityReference('@{X111|NOUN,sing}')).toStrictEqual({entity: 'X111', form: 'NOUN,sing'});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syntactic reference',
|
||||||
|
() => {
|
||||||
|
expect(parseSyntacticReference('@{1|test test}')).toStrictEqual({offset: 1, nominal: 'test test'});
|
||||||
|
expect(parseSyntacticReference('@{101|test test}')).toStrictEqual({offset: 101, nominal: 'test test'});
|
||||||
|
expect(parseSyntacticReference('@{-1|test test}')).toStrictEqual({offset: -1, nominal: 'test test'});
|
||||||
|
expect(parseSyntacticReference('@{-99|test test}')).toStrictEqual({offset: -99, nominal: 'test test'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// Module: Natural language model declarations.
|
/**
|
||||||
|
* Module: Natural language model declarations.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents API result for text output.
|
* Represents API result for text output.
|
||||||
|
@ -11,9 +13,6 @@ export interface ITextResult {
|
||||||
* Represents single unit of language Morphology.
|
* Represents single unit of language Morphology.
|
||||||
*/
|
*/
|
||||||
export enum Grammeme {
|
export enum Grammeme {
|
||||||
// Неизвестная граммема
|
|
||||||
UNKN = 'UNKN',
|
|
||||||
|
|
||||||
// Части речи
|
// Части речи
|
||||||
NOUN = 'NOUN', ADJF = 'ADJF', ADJS = 'ADJS', COMP = 'COMP',
|
NOUN = 'NOUN', ADJF = 'ADJF', ADJS = 'ADJS', COMP = 'COMP',
|
||||||
VERB = 'VERB', INFN = 'INFN', PRTF = 'PRTF', PRTS = 'PRTS',
|
VERB = 'VERB', INFN = 'INFN', PRTF = 'PRTF', PRTS = 'PRTS',
|
||||||
|
@ -204,17 +203,14 @@ export const VerbGrams = [
|
||||||
/**
|
/**
|
||||||
* Represents {@link Grammeme} parse data.
|
* Represents {@link Grammeme} parse data.
|
||||||
*/
|
*/
|
||||||
export interface IGramData {
|
export type GramData = Grammeme | string;
|
||||||
type: Grammeme
|
|
||||||
data: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents specific wordform attached to {@link Grammeme}s.
|
* Represents specific wordform attached to {@link Grammeme}s.
|
||||||
*/
|
*/
|
||||||
export interface IWordForm {
|
export interface IWordForm {
|
||||||
text: string
|
text: string
|
||||||
grams: IGramData[]
|
grams: GramData[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -232,16 +228,6 @@ export interface ILexemeData {
|
||||||
items: IWordFormPlain[]
|
items: IWordFormPlain[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Equality comparator for {@link IGramData}. Compares text data for unknown grammemes
|
|
||||||
*/
|
|
||||||
export function matchGrammeme(left: IGramData, right: IGramData): boolean {
|
|
||||||
if (left.type !== right.type) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return left.type !== Grammeme.UNKN || left.data === right.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Equality comparator for {@link IWordForm}. Compares a set of Grammemes attached to wordforms
|
* Equality comparator for {@link IWordForm}. Compares a set of Grammemes attached to wordforms
|
||||||
*/
|
*/
|
||||||
|
@ -250,41 +236,43 @@ export function matchWordForm(left: IWordForm, right: IWordForm): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (let index = 0; index < left.grams.length; ++index) {
|
for (let index = 0; index < left.grams.length; ++index) {
|
||||||
if (!matchGrammeme(left.grams[index], right.grams[index])) {
|
if (left.grams[index] !== right.grams[index]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSingleGrammeme(text: string): IGramData {
|
function parseSingleGrammeme(text: string): GramData {
|
||||||
if (Object.values(Grammeme).includes(text as Grammeme)) {
|
if (Object.values(Grammeme).includes(text as Grammeme)) {
|
||||||
return {
|
return text as Grammeme;
|
||||||
data: text,
|
|
||||||
type: text as Grammeme
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
return text;
|
||||||
data: text,
|
|
||||||
type: Grammeme.UNKN
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sortGrammemes<TData extends IGramData>(input: TData[]): TData[] {
|
/**
|
||||||
const result: TData[] = [];
|
* Compares {@link GramData} based on Grammeme enum and alpha order for strings.
|
||||||
Object.values(Grammeme).forEach(
|
*/
|
||||||
gram => {
|
export function compareGrammemes(left: GramData, right: GramData): number {
|
||||||
const item = input.find(data => data.type === gram);
|
const indexLeft = Object.values(Grammeme).findIndex(gram => gram === left as Grammeme);
|
||||||
if (item) {
|
const indexRight = Object.values(Grammeme).findIndex(gram => gram === right as Grammeme);
|
||||||
result.push(item);
|
if (indexLeft === -1 && indexRight === -1) {
|
||||||
|
return left.localeCompare(right);
|
||||||
|
} else if (indexLeft === -1 && indexRight !== -1) {
|
||||||
|
return 1;
|
||||||
|
} else if (indexLeft !== -1 && indexRight === -1) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return indexLeft - indexRight;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseGrammemes(termForm: string): IGramData[] {
|
/**
|
||||||
const result: IGramData[] = [];
|
* Transforms {@link Grammeme} enumeration to {@link GramData}.
|
||||||
|
*/
|
||||||
|
export function parseGrammemes(termForm: string): GramData[] {
|
||||||
|
const result: GramData[] = [];
|
||||||
const chunks = termForm.split(',');
|
const chunks = termForm.split(',');
|
||||||
chunks.forEach(chunk => {
|
chunks.forEach(chunk => {
|
||||||
chunk = chunk.trim();
|
chunk = chunk.trim();
|
||||||
|
@ -292,7 +280,7 @@ export function parseGrammemes(termForm: string): IGramData[] {
|
||||||
result.push(parseSingleGrammeme(chunk));
|
result.push(parseSingleGrammeme(chunk));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return sortGrammemes(result);
|
return result.sort(compareGrammemes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====== Reference resolution =====
|
// ====== Reference resolution =====
|
||||||
|
@ -353,3 +341,29 @@ export interface IResolutionData {
|
||||||
output: string
|
output: string
|
||||||
refs: IResolvedReference[]
|
refs: IResolvedReference[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts {@link IEntityReference} from string representation.
|
||||||
|
*
|
||||||
|
* @param text - Reference text in a valid pattern. Must fit format '\@\{GLOBAL_ID|GRAMMEMES\}'
|
||||||
|
*/
|
||||||
|
export function parseEntityReference(text: string): IEntityReference {
|
||||||
|
const blocks = text.slice(2, text.length - 1).split('|');
|
||||||
|
return {
|
||||||
|
entity: blocks[0].trim(),
|
||||||
|
form: blocks[1].trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts {@link ISyntacticReference} from string representation.
|
||||||
|
*
|
||||||
|
* @param text - Reference text in a valid pattern. Must fit format '\@\{OFFSET|NOMINAL_FORM\}'
|
||||||
|
*/
|
||||||
|
export function parseSyntacticReference(text: string): ISyntacticReference {
|
||||||
|
const blocks = text.slice(2, text.length - 1).split('|');
|
||||||
|
return {
|
||||||
|
offset: Number(blocks[0].trim()),
|
||||||
|
nominal: blocks[1].trim()
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,15 +10,13 @@ import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon, CrossI
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import useConceptText from '../../hooks/useConceptText';
|
import useConceptText from '../../hooks/useConceptText';
|
||||||
import {
|
import {
|
||||||
Grammeme, GrammemeGroups, ITextRequest, IWordForm,
|
GramData, Grammeme, GrammemeGroups, ITextRequest, IWordForm,
|
||||||
IWordFormPlain,
|
IWordFormPlain, matchWordForm, NounGrams, parseGrammemes, VerbGrams
|
||||||
matchWordForm, NounGrams, parseGrammemes,
|
|
||||||
sortGrammemes, VerbGrams
|
|
||||||
} from '../../models/language';
|
} from '../../models/language';
|
||||||
import { IConstituenta, TermForm } from '../../models/rsform';
|
import { IConstituenta, TermForm } from '../../models/rsform';
|
||||||
import { colorfgGrammeme } from '../../utils/color';
|
import { colorfgGrammeme } from '../../utils/color';
|
||||||
import { labelGrammeme } from '../../utils/labels';
|
import { labelGrammeme } from '../../utils/labels';
|
||||||
import { IGrammemeOption, SelectorGrammemesList, SelectorGrammems } from '../../utils/selectors';
|
import { compareGrammemeOptions,IGrammemeOption, SelectorGrammemesList, SelectorGrammems } from '../../utils/selectors';
|
||||||
|
|
||||||
interface DlgEditTermProps {
|
interface DlgEditTermProps {
|
||||||
hideWindow: () => void
|
hideWindow: () => void
|
||||||
|
@ -44,7 +42,7 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
forms.forEach(
|
forms.forEach(
|
||||||
({text, grams}) => result.push({
|
({text, grams}) => result.push({
|
||||||
text: text,
|
text: text,
|
||||||
tags: grams.map(gram => gram.data).join(',')
|
tags: grams.join(',')
|
||||||
}));
|
}));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -67,32 +65,32 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
// Filter grammemes when input changes
|
// Filter grammemes when input changes
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
let newFilter: Grammeme[] = [];
|
let newFilter: GramData[] = [];
|
||||||
inputGrams.forEach(({type: gram}) => {
|
inputGrams.forEach(({value: gram}) => {
|
||||||
if (!newFilter.includes(gram)) {
|
if (!newFilter.includes(gram)) {
|
||||||
if (NounGrams.includes(gram)) {
|
if (NounGrams.includes(gram as Grammeme)) {
|
||||||
newFilter.push(...NounGrams);
|
newFilter.push(...NounGrams);
|
||||||
}
|
}
|
||||||
if (VerbGrams.includes(gram)) {
|
if (VerbGrams.includes(gram as Grammeme)) {
|
||||||
newFilter.push(...VerbGrams);
|
newFilter.push(...VerbGrams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inputGrams.forEach(({type: gram}) =>
|
inputGrams.forEach(({value: gram}) =>
|
||||||
GrammemeGroups.forEach(group => {
|
GrammemeGroups.forEach(group => {
|
||||||
if (group.includes(gram)) {
|
if (group.includes(gram as Grammeme)) {
|
||||||
newFilter = newFilter.filter(item => !group.includes(item) || item === gram);
|
newFilter = newFilter.filter(item => !group.includes(item as Grammeme) || item === gram);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
newFilter.push(...inputGrams.map(({type: gram}) => gram));
|
newFilter.push(...inputGrams.map(({value}) => value));
|
||||||
if (newFilter.length === 0) {
|
if (newFilter.length === 0) {
|
||||||
newFilter = [...VerbGrams, ...NounGrams];
|
newFilter = [...VerbGrams, ...NounGrams];
|
||||||
}
|
}
|
||||||
|
|
||||||
newFilter = [... new Set(newFilter)];
|
newFilter = [... new Set(newFilter)];
|
||||||
setOptions(SelectorGrammems.filter(({type: gram}) => newFilter.includes(gram)));
|
setOptions(SelectorGrammems.filter(({value}) => newFilter.includes(value)));
|
||||||
}, [inputGrams]);
|
}, [inputGrams]);
|
||||||
|
|
||||||
const handleSubmit = () => onSave(getData());
|
const handleSubmit = () => onSave(getData());
|
||||||
|
@ -100,10 +98,7 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
function handleAddForm() {
|
function handleAddForm() {
|
||||||
const newForm: IWordForm = {
|
const newForm: IWordForm = {
|
||||||
text: inputText,
|
text: inputText,
|
||||||
grams: inputGrams.map(item => ({
|
grams: inputGrams.map(item => item.value)
|
||||||
type: item.type,
|
|
||||||
data: item.data
|
|
||||||
}))
|
|
||||||
};
|
};
|
||||||
setForms(forms => [
|
setForms(forms => [
|
||||||
newForm,
|
newForm,
|
||||||
|
@ -127,7 +122,7 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
|
|
||||||
function handleRowClicked(form: IWordForm) {
|
function handleRowClicked(form: IWordForm) {
|
||||||
setInputText(form.text);
|
setInputText(form.text);
|
||||||
setInputGrams(SelectorGrammems.filter(gram => form.grams.find(test => test.type === gram.type)));
|
setInputGrams(SelectorGrammems.filter(gram => form.grams.find(test => test === gram.value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleResetForm() {
|
function handleResetForm() {
|
||||||
|
@ -138,7 +133,7 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
function handleInflect() {
|
function handleInflect() {
|
||||||
const data: IWordFormPlain = {
|
const data: IWordFormPlain = {
|
||||||
text: term,
|
text: term,
|
||||||
grams: inputGrams.map(gram => gram.data).join(',')
|
grams: inputGrams.map(gram => gram.value).join(',')
|
||||||
}
|
}
|
||||||
textProcessor.inflect(data, response => setInputText(response.result));
|
textProcessor.inflect(data, response => setInputText(response.result));
|
||||||
}
|
}
|
||||||
|
@ -149,7 +144,7 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
}
|
}
|
||||||
textProcessor.parse(data, response => {
|
textProcessor.parse(data, response => {
|
||||||
const grams = parseGrammemes(response.result);
|
const grams = parseGrammemes(response.result);
|
||||||
setInputGrams(SelectorGrammems.filter(gram => grams.find(test => test.type === gram.type)));
|
setInputGrams(SelectorGrammems.filter(gram => grams.find(test => test === gram.value)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +161,7 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
const newForms: IWordForm[] = response.items.map(
|
const newForms: IWordForm[] = response.items.map(
|
||||||
form => ({
|
form => ({
|
||||||
text: form.text,
|
text: form.text,
|
||||||
grams: parseGrammemes(form.grams).filter(gram => SelectorGrammemesList.find(item => item === gram.type))
|
grams: parseGrammemes(form.grams).filter(gram => SelectorGrammemesList.find(item => item === gram as Grammeme))
|
||||||
}));
|
}));
|
||||||
setForms(forms => [
|
setForms(forms => [
|
||||||
...newForms,
|
...newForms,
|
||||||
|
@ -196,13 +191,13 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
{ props.getValue().map(
|
{ props.getValue().map(
|
||||||
gram =>
|
gram =>
|
||||||
<div
|
<div
|
||||||
key={`${props.cell.id}-${gram.type}`}
|
key={`${props.cell.id}-${gram}`}
|
||||||
className='min-w-[3rem] px-1 text-sm text-center rounded-md whitespace-nowrap'
|
className='min-w-[3rem] px-1 text-sm text-center rounded-md whitespace-nowrap'
|
||||||
title=''
|
title=''
|
||||||
style={{
|
style={{
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
borderColor: colorfgGrammeme(gram.type, colors),
|
borderColor: colorfgGrammeme(gram, colors),
|
||||||
color: colorfgGrammeme(gram.type, colors),
|
color: colorfgGrammeme(gram, colors),
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
backgroundColor: colors.bgInput
|
backgroundColor: colors.bgInput
|
||||||
}}
|
}}
|
||||||
|
@ -260,7 +255,7 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
value={inputText}
|
value={inputText}
|
||||||
onChange={event => setInputText(event.target.value)}
|
onChange={event => setInputText(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between select-none'>
|
||||||
<div className='flex items-center justify-start'>
|
<div className='flex items-center justify-start'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Добавить словоформу'
|
tooltip='Добавить словоформу'
|
||||||
|
@ -281,6 +276,9 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
onClick={handleGenerateLexeme}
|
onClick={handleGenerateLexeme}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='text-sm'>
|
||||||
|
Словоформ: {forms.length}
|
||||||
|
</div>
|
||||||
<div className='flex items-center justify-start'>
|
<div className='flex items-center justify-start'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Генерировать словоформу'
|
tooltip='Генерировать словоформу'
|
||||||
|
@ -304,7 +302,7 @@ function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
||||||
|
|
||||||
value={inputGrams}
|
value={inputGrams}
|
||||||
isDisabled={textProcessor.loading}
|
isDisabled={textProcessor.loading}
|
||||||
onChange={newValue => setInputGrams(sortGrammemes([...newValue]))}
|
onChange={newValue => setInputGrams([...newValue].sort(compareGrammemeOptions))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
219
rsconcept/frontend/src/utils/codemirror.ts
Normal file
219
rsconcept/frontend/src/utils/codemirror.ts
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
import { NodeType, Tree, TreeCursor } from '@lezer/common'
|
||||||
|
|
||||||
|
import { IEntityReference, ISyntacticReference, parseGrammemes } from '../models/language'
|
||||||
|
import { IConstituenta } from '../models/rsform'
|
||||||
|
import { colorfgGrammeme,IColorTheme } from './color'
|
||||||
|
import { describeConstituentaTerm, labelCstTypification, labelGrammeme } from './labels'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents syntax tree node data.
|
||||||
|
*/
|
||||||
|
export interface SyntaxNode {
|
||||||
|
type: NodeType
|
||||||
|
from: number
|
||||||
|
to: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents syntax tree cursor data.
|
||||||
|
*/
|
||||||
|
export interface CursorNode
|
||||||
|
extends SyntaxNode {
|
||||||
|
isLeaf: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function cursorNode({ type, from, to }: TreeCursor, isLeaf = false): CursorNode {
|
||||||
|
return { type, from, to, isLeaf }
|
||||||
|
}
|
||||||
|
|
||||||
|
type TreeTraversalOptions = {
|
||||||
|
beforeEnter?: (cursor: TreeCursor) => void
|
||||||
|
onEnter: (node: CursorNode) => false | void
|
||||||
|
onLeave?: (node: CursorNode) => false | void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements depth-first traversal.
|
||||||
|
*/
|
||||||
|
export function traverseTree(tree: Tree, { beforeEnter, onEnter, onLeave, }: TreeTraversalOptions) {
|
||||||
|
const cursor = tree.cursor();
|
||||||
|
for (;;) {
|
||||||
|
let node = cursorNode(cursor)
|
||||||
|
let leave = false
|
||||||
|
const enter = !node.type.isAnonymous
|
||||||
|
if (enter && beforeEnter) beforeEnter(cursor)
|
||||||
|
node.isLeaf = !cursor.firstChild()
|
||||||
|
if (enter) {
|
||||||
|
leave = true
|
||||||
|
if (onEnter(node) === false) return
|
||||||
|
}
|
||||||
|
if (!node.isLeaf) continue
|
||||||
|
for (;;) {
|
||||||
|
node = cursorNode(cursor, node.isLeaf)
|
||||||
|
if (leave && onLeave) if (onLeave(node) === false) return;
|
||||||
|
leave = cursor.type.isAnonymous
|
||||||
|
node.isLeaf = false
|
||||||
|
if (cursor.nextSibling()) break;
|
||||||
|
if (!cursor.parent()) return;
|
||||||
|
leave = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints tree to compact string.
|
||||||
|
*/
|
||||||
|
export function printTree(tree: Tree): string {
|
||||||
|
const state = {
|
||||||
|
output: '',
|
||||||
|
prefixes: [] as string[]
|
||||||
|
}
|
||||||
|
traverseTree(tree, {
|
||||||
|
onEnter: node => {
|
||||||
|
state.output += '[';
|
||||||
|
state.output += node.type.name;
|
||||||
|
},
|
||||||
|
onLeave: () => {
|
||||||
|
state.output += ']';
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return state.output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reteives a list of all nodes, containing given range and corresponding to a filter.
|
||||||
|
*/
|
||||||
|
export function findEnvelopingNodes(start: number, finish: number, tree: Tree, filter?: number[]): SyntaxNode[] {
|
||||||
|
const result: SyntaxNode[] = [];
|
||||||
|
tree.cursor().iterate(
|
||||||
|
node => {
|
||||||
|
if (
|
||||||
|
(!filter || filter.includes(node.type.id)) &&
|
||||||
|
node.to >= start && node.from <= finish
|
||||||
|
) {
|
||||||
|
result.push({
|
||||||
|
type: node.type,
|
||||||
|
to: node.to,
|
||||||
|
from: node.from
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reteives a list of all nodes, contained in given range and corresponding to a filter.
|
||||||
|
*/
|
||||||
|
export function findContainedNodes(start: number, finish: number, tree: Tree, filter?: number[]): SyntaxNode[] {
|
||||||
|
const result: SyntaxNode[] = [];
|
||||||
|
tree.cursor().iterate(
|
||||||
|
node => {
|
||||||
|
if (
|
||||||
|
(!filter || filter.includes(node.type.id)) &&
|
||||||
|
node.to <= start && node.from >= finish
|
||||||
|
) {
|
||||||
|
result.push({
|
||||||
|
type: node.type,
|
||||||
|
to: node.to,
|
||||||
|
from: node.from
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DOM tooltip for {@link Constituenta}.
|
||||||
|
*/
|
||||||
|
export function domTooltipConstituenta(cst: IConstituenta) {
|
||||||
|
const dom = document.createElement('div');
|
||||||
|
dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-tooltip text-sm px-2 py-2';
|
||||||
|
|
||||||
|
const alias = document.createElement('p');
|
||||||
|
alias.innerHTML = `<b>${cst.alias}:</b> ${labelCstTypification(cst)}`;
|
||||||
|
dom.appendChild(alias);
|
||||||
|
|
||||||
|
if (cst.term_resolved) {
|
||||||
|
const term = document.createElement('p');
|
||||||
|
term.innerHTML = `<b>Термин:</b> ${cst.term_resolved}`;
|
||||||
|
dom.appendChild(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cst.definition_formal) {
|
||||||
|
const expression = document.createElement('p');
|
||||||
|
expression.innerHTML = `<b>Выражение:</b> ${cst.definition_formal}`;
|
||||||
|
dom.appendChild(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cst.definition_resolved) {
|
||||||
|
const definition = document.createElement('p');
|
||||||
|
definition.innerHTML = `<b>Определение:</b> ${cst.definition_resolved}`;
|
||||||
|
dom.appendChild(definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cst.convention) {
|
||||||
|
const convention = document.createElement('p');
|
||||||
|
convention.innerHTML = `<b>Конвенция:</b> ${cst.convention}`;
|
||||||
|
dom.appendChild(convention);
|
||||||
|
}
|
||||||
|
return { dom: dom };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DOM tooltip for {@link IEntityReference}.
|
||||||
|
*/
|
||||||
|
export function domTooltipEntityReference(ref: IEntityReference, cst: IConstituenta | undefined, colors: IColorTheme) {
|
||||||
|
const DIMENSIONS = 'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-tooltip px-2 py-2';
|
||||||
|
const LAYOUT = 'flex flex-col gap-1 overflow-y-auto'
|
||||||
|
|
||||||
|
const dom = document.createElement('div');
|
||||||
|
dom.className = `${DIMENSIONS} ${LAYOUT} border shadow-md text-sm select-none cursor-auto`;
|
||||||
|
|
||||||
|
const term = document.createElement('p');
|
||||||
|
term.innerHTML = `<b>${ref.entity}:</b> ${describeConstituentaTerm(cst)}`;
|
||||||
|
dom.appendChild(term);
|
||||||
|
|
||||||
|
const grams = document.createElement('div');
|
||||||
|
grams.className = 'flex flex-wrap gap-1';
|
||||||
|
parseGrammemes(ref.form).forEach(
|
||||||
|
gramStr => {
|
||||||
|
const gram = document.createElement('div');
|
||||||
|
gram.id =`tooltip-${gramStr}`;
|
||||||
|
gram.className='min-w-[3rem] px-1 text-sm text-center rounded-md whitespace-nowrap';
|
||||||
|
gram.style.borderWidth = '1px';
|
||||||
|
gram.style.borderColor = colorfgGrammeme(gramStr, colors);
|
||||||
|
gram.style.color = colorfgGrammeme(gramStr, colors);
|
||||||
|
gram.style.fontWeight = '600';
|
||||||
|
gram.style.backgroundColor = colors.bgInput;
|
||||||
|
gram.innerText = labelGrammeme(gramStr);
|
||||||
|
grams.appendChild(gram);
|
||||||
|
});
|
||||||
|
dom.appendChild(grams);
|
||||||
|
|
||||||
|
return { dom: dom };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DOM tooltip for {@link ISyntacticReference}.
|
||||||
|
*/
|
||||||
|
export function domTooltipSyntacticReference(ref: ISyntacticReference) {
|
||||||
|
const DIMENSIONS = 'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-tooltip px-2 py-2';
|
||||||
|
const LAYOUT = 'flex flex-col gap-1 overflow-y-auto'
|
||||||
|
|
||||||
|
const dom = document.createElement('div');
|
||||||
|
dom.className = `${DIMENSIONS} ${LAYOUT} border shadow-md text-sm select-none cursor-auto`;
|
||||||
|
|
||||||
|
const title = document.createElement('p');
|
||||||
|
title.innerHTML = '<b>Синтаксическая ссылка</b>';
|
||||||
|
dom.appendChild(title);
|
||||||
|
|
||||||
|
const offset = document.createElement('p');
|
||||||
|
offset.innerHTML = `<b>Смещение:</b> ${ref.offset}`;
|
||||||
|
dom.appendChild(offset);
|
||||||
|
|
||||||
|
const nominal = document.createElement('p');
|
||||||
|
nominal.innerHTML = `<b>Начальная форма:</b> ${ref.nominal}`;
|
||||||
|
dom.appendChild(nominal);
|
||||||
|
|
||||||
|
return { dom: dom };
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
// =========== Modules contains all dynamic color definitions ==========
|
// =========== Modules contains all dynamic color definitions ==========
|
||||||
|
|
||||||
import { Grammeme, NounGrams, PartOfSpeech, VerbGrams } from '../models/language'
|
import { GramData, Grammeme, NounGrams, PartOfSpeech, VerbGrams } from '../models/language'
|
||||||
import { CstClass, ExpressionStatus } from '../models/rsform'
|
import { CstClass, ExpressionStatus } from '../models/rsform'
|
||||||
import { ISyntaxTreeNode, TokenID } from '../models/rslang'
|
import { ISyntaxTreeNode, TokenID } from '../models/rslang'
|
||||||
|
|
||||||
|
@ -385,30 +385,31 @@ export function colorbgCstClass(cstClass: CstClass, colors: IColorTheme): string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function colorfgGrammeme(gram: Grammeme, colors: IColorTheme): string {
|
export function colorfgGrammeme(gram: GramData, colors: IColorTheme): string {
|
||||||
if (PartOfSpeech.includes(gram)) {
|
if (PartOfSpeech.includes(gram as Grammeme)) {
|
||||||
return colors.fgBlue;
|
return colors.fgBlue;
|
||||||
}
|
}
|
||||||
if (NounGrams.includes(gram)) {
|
if (NounGrams.includes(gram as Grammeme)) {
|
||||||
return colors.fgGreen;
|
return colors.fgGreen;
|
||||||
}
|
}
|
||||||
if (VerbGrams.includes(gram)) {
|
if (VerbGrams.includes(gram as Grammeme)) {
|
||||||
return colors.fgTeal;
|
return colors.fgTeal;
|
||||||
}
|
}
|
||||||
if (gram === Grammeme.UNKN) {
|
if (!Object.values(Grammeme).includes(gram as Grammeme)) {
|
||||||
return colors.fgRed;
|
return colors.fgRed;
|
||||||
}
|
} else {
|
||||||
return colors.fgPurple;
|
return colors.fgPurple;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function colorbgGrammeme(gram: Grammeme, colors: IColorTheme): string {
|
export function colorbgGrammeme(gram: GramData, colors: IColorTheme): string {
|
||||||
if (PartOfSpeech.includes(gram)) {
|
if (PartOfSpeech.includes(gram as Grammeme)) {
|
||||||
return colors.bgBlue;
|
return colors.bgBlue;
|
||||||
}
|
}
|
||||||
if (NounGrams.includes(gram)) {
|
if (NounGrams.includes(gram as Grammeme)) {
|
||||||
return colors.bgGreen;
|
return colors.bgGreen;
|
||||||
}
|
}
|
||||||
if (VerbGrams.includes(gram)) {
|
if (VerbGrams.includes(gram as Grammeme)) {
|
||||||
return colors.bgTeal;
|
return colors.bgTeal;
|
||||||
}
|
}
|
||||||
return colors.bgInput;
|
return colors.bgInput;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// =========== Modules contains all text descriptors ==========
|
// =========== Modules contains all text descriptors ==========
|
||||||
|
|
||||||
import { Grammeme,IGramData } from '../models/language';
|
import { GramData,Grammeme } from '../models/language';
|
||||||
import { CstMatchMode, DependencyMode, HelpTopic } from '../models/miscelanious';
|
import { CstMatchMode, DependencyMode, HelpTopic } from '../models/miscelanious';
|
||||||
import { CstClass, CstType, ExpressionStatus, IConstituenta } from '../models/rsform';
|
import { CstClass, CstType, ExpressionStatus, IConstituenta } from '../models/rsform';
|
||||||
import { IFunctionArg, IRSErrorDescription, ISyntaxTreeNode, ParsingStatus, RSErrorType, TokenID } from '../models/rslang';
|
import { IFunctionArg, IRSErrorDescription, ISyntaxTreeNode, ParsingStatus, RSErrorType, TokenID } from '../models/rslang';
|
||||||
|
@ -23,6 +23,17 @@ export function describeConstituenta(cst: IConstituenta): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function describeConstituentaTerm(cst: IConstituenta | undefined): string {
|
||||||
|
if (!cst) {
|
||||||
|
return '!Конституента отсутствует!';
|
||||||
|
}
|
||||||
|
if (!cst.term_resolved) {
|
||||||
|
return '!Пустой термин!';
|
||||||
|
} else {
|
||||||
|
return cst.term_resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function labelConstituenta(cst: IConstituenta) {
|
export function labelConstituenta(cst: IConstituenta) {
|
||||||
return `${cst.alias}: ${describeConstituenta(cst)}`;
|
return `${cst.alias}: ${describeConstituenta(cst)}`;
|
||||||
}
|
}
|
||||||
|
@ -346,8 +357,10 @@ export function labelSyntaxTree(node: ISyntaxTreeNode): string {
|
||||||
return 'UNKNOWN ' + String(node.typeID);
|
return 'UNKNOWN ' + String(node.typeID);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function labelGrammeme(gram: IGramData): string {
|
export function labelGrammeme(gram: GramData): string {
|
||||||
switch (gram.type) {
|
switch (gram) {
|
||||||
|
default: return `Неизв: ${gram}`;
|
||||||
|
|
||||||
case Grammeme.NOUN: return 'ЧР: сущ';
|
case Grammeme.NOUN: return 'ЧР: сущ';
|
||||||
case Grammeme.VERB: return 'ЧР: глагол';
|
case Grammeme.VERB: return 'ЧР: глагол';
|
||||||
case Grammeme.INFN: return 'ЧР: глагол инф';
|
case Grammeme.INFN: return 'ЧР: глагол инф';
|
||||||
|
@ -411,8 +424,6 @@ export function labelGrammeme(gram: IGramData): string {
|
||||||
case Grammeme.Slng: return 'Стиль: жаргон';
|
case Grammeme.Slng: return 'Стиль: жаргон';
|
||||||
case Grammeme.Arch: return 'Стиль: устаревший';
|
case Grammeme.Arch: return 'Стиль: устаревший';
|
||||||
case Grammeme.Litr: return 'Стиль: литературный';
|
case Grammeme.Litr: return 'Стиль: литературный';
|
||||||
|
|
||||||
case Grammeme.UNKN: return `Неизв: ${gram.data}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { NodeType, Tree, TreeCursor } from '@lezer/common'
|
|
||||||
|
|
||||||
export type CursorNode = {
|
|
||||||
type: NodeType
|
|
||||||
from: number
|
|
||||||
to: number
|
|
||||||
isLeaf: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
function cursorNode({ type, from, to }: TreeCursor, isLeaf = false): CursorNode {
|
|
||||||
return { type, from, to, isLeaf }
|
|
||||||
}
|
|
||||||
|
|
||||||
type TreeTraversalOptions = {
|
|
||||||
beforeEnter?: (cursor: TreeCursor) => void
|
|
||||||
onEnter: (node: CursorNode) => false | void
|
|
||||||
onLeave?: (node: CursorNode) => false | void
|
|
||||||
}
|
|
||||||
|
|
||||||
export function traverseTree(tree: Tree, { beforeEnter, onEnter, onLeave, }: TreeTraversalOptions) {
|
|
||||||
const cursor = tree.cursor();
|
|
||||||
for (;;) {
|
|
||||||
let node = cursorNode(cursor)
|
|
||||||
let leave = false
|
|
||||||
const enter = !node.type.isAnonymous
|
|
||||||
if (enter && beforeEnter) beforeEnter(cursor)
|
|
||||||
node.isLeaf = !cursor.firstChild()
|
|
||||||
if (enter) {
|
|
||||||
leave = true
|
|
||||||
if (onEnter(node) === false) return
|
|
||||||
}
|
|
||||||
if (!node.isLeaf) continue
|
|
||||||
for (;;) {
|
|
||||||
node = cursorNode(cursor, node.isLeaf)
|
|
||||||
if (leave && onLeave) if (onLeave(node) === false) return;
|
|
||||||
leave = cursor.type.isAnonymous
|
|
||||||
node.isLeaf = false
|
|
||||||
if (cursor.nextSibling()) break;
|
|
||||||
if (!cursor.parent()) return;
|
|
||||||
leave = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function printTree(tree: Tree): string {
|
|
||||||
const state = {
|
|
||||||
output: '',
|
|
||||||
prefixes: [] as string[]
|
|
||||||
}
|
|
||||||
traverseTree(tree, {
|
|
||||||
onEnter: node => {
|
|
||||||
state.output += '[';
|
|
||||||
state.output += node.type.name;
|
|
||||||
},
|
|
||||||
onLeave: () => {
|
|
||||||
state.output += ']';
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return state.output;
|
|
||||||
}
|
|
|
@ -1,13 +1,15 @@
|
||||||
// Module: Selector maps
|
// Module: Selector maps
|
||||||
import { LayoutTypes } from 'reagraph';
|
import { LayoutTypes } from 'reagraph';
|
||||||
|
|
||||||
import { Grammeme, IGramData } from '../models/language';
|
import { compareGrammemes,type GramData, Grammeme } from '../models/language';
|
||||||
import { CstType } from '../models/rsform';
|
import { CstType } from '../models/rsform';
|
||||||
import { ColoringScheme } from '../pages/RSFormPage/EditorTermGraph';
|
import { ColoringScheme } from '../pages/RSFormPage/EditorTermGraph';
|
||||||
import { labelGrammeme } from './labels';
|
import { labelGrammeme } from './labels';
|
||||||
import { labelCstType } from './labels';
|
import { labelCstType } from './labels';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents options for GraphLayout selector.
|
||||||
|
*/
|
||||||
export const SelectorGraphLayout: { value: LayoutTypes, label: string }[] = [
|
export const SelectorGraphLayout: { value: LayoutTypes, label: string }[] = [
|
||||||
{ value: 'treeTd2d', label: 'Граф: ДеревоВ 2D' },
|
{ value: 'treeTd2d', label: 'Граф: ДеревоВ 2D' },
|
||||||
{ value: 'treeTd3d', label: 'Граф: ДеревоВ 3D' },
|
{ value: 'treeTd3d', label: 'Граф: ДеревоВ 3D' },
|
||||||
|
@ -24,12 +26,18 @@ export const SelectorGraphLayout: { value: LayoutTypes, label: string }[] = [
|
||||||
// { value: 'hierarchicalLr', label: 'hierarchicalLr'}
|
// { value: 'hierarchicalLr', label: 'hierarchicalLr'}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents options for {@link ColoringScheme} selector.
|
||||||
|
*/
|
||||||
export const SelectorGraphColoring: { value: ColoringScheme, label: string }[] = [
|
export const SelectorGraphColoring: { value: ColoringScheme, label: string }[] = [
|
||||||
{ value: 'none', label: 'Цвет: моно' },
|
{ value: 'none', label: 'Цвет: моно' },
|
||||||
{ value: 'status', label: 'Цвет: статус' },
|
{ value: 'status', label: 'Цвет: статус' },
|
||||||
{ value: 'type', label: 'Цвет: класс' },
|
{ value: 'type', label: 'Цвет: класс' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents options for {@link CstType} selector.
|
||||||
|
*/
|
||||||
export const SelectorCstType = (
|
export const SelectorCstType = (
|
||||||
Object.values(CstType)).map(
|
Object.values(CstType)).map(
|
||||||
typeStr => ({
|
typeStr => ({
|
||||||
|
@ -38,11 +46,24 @@ export const SelectorCstType = (
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface IGrammemeOption extends IGramData {
|
/**
|
||||||
value: string
|
* Represents single option for {@link Grammeme} selector.
|
||||||
|
*/
|
||||||
|
export interface IGrammemeOption {
|
||||||
|
value: GramData
|
||||||
label: string
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares {@link IGrammemeOption} based on Grammeme comparison.
|
||||||
|
*/
|
||||||
|
export function compareGrammemeOptions(left: IGrammemeOption, right: IGrammemeOption): number {
|
||||||
|
return compareGrammemes(left.value, right.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents list of {@link Grammeme}s available in reference construction.
|
||||||
|
*/
|
||||||
export const SelectorGrammemesList = [
|
export const SelectorGrammemesList = [
|
||||||
Grammeme.NOUN, Grammeme.VERB,
|
Grammeme.NOUN, Grammeme.VERB,
|
||||||
|
|
||||||
|
@ -62,11 +83,12 @@ export const SelectorGrammemesList = [
|
||||||
Grammeme.pssv, Grammeme.actv,
|
Grammeme.pssv, Grammeme.actv,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents options for {@link Grammeme} selector.
|
||||||
|
*/
|
||||||
export const SelectorGrammems: IGrammemeOption[] =
|
export const SelectorGrammems: IGrammemeOption[] =
|
||||||
SelectorGrammemesList.map(
|
SelectorGrammemesList.map(
|
||||||
gram => ({
|
gram => ({
|
||||||
type: gram,
|
value: gram,
|
||||||
data: gram as string,
|
label: labelGrammeme(gram)
|
||||||
value: gram as string,
|
|
||||||
label: labelGrammeme({type: gram, data: ''} as IGramData)
|
|
||||||
}));
|
}));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user