Rename, fix autocomplete and singleline, etc...
@@ -7,8 +7,8 @@
|
|||||||
</scripts>
|
</scripts>
|
||||||
<node-interpreter value="project" />
|
<node-interpreter value="project" />
|
||||||
<envs>
|
<envs>
|
||||||
<env name="DATABASE_URL" value="sqlite://$USER_HOME$/Library/Application%20Support/co.schier.twosomnia/db.sqlite" />
|
<env name="DATABASE_URL" value="sqlite://$USER_HOME$/Library/Application%20Support/co.schier.yaak/db.sqlite" />
|
||||||
</envs>
|
</envs>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 106 KiB |
@@ -6,7 +6,7 @@ distDir = "../dist"
|
|||||||
withGlobalTauri = false
|
withGlobalTauri = false
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
productName = "Twosomnia"
|
productName = "Yaak"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
[tauri.allowlist]
|
[tauri.allowlist]
|
||||||
@@ -34,7 +34,7 @@ icon = [
|
|||||||
"icons/icon.icns",
|
"icons/icon.icns",
|
||||||
"icons/icon.ico"
|
"icons/icon.ico"
|
||||||
]
|
]
|
||||||
identifier = "co.schier.twosomnia"
|
identifier = "co.schier.yaak"
|
||||||
longDescription = ""
|
longDescription = ""
|
||||||
resources = [ "plugins/*", "migrations/*" ]
|
resources = [ "plugins/*", "migrations/*" ]
|
||||||
shortDescription = ""
|
shortDescription = ""
|
||||||
@@ -63,7 +63,7 @@ dialog = true
|
|||||||
fullscreen = false
|
fullscreen = false
|
||||||
height = 800
|
height = 800
|
||||||
resizable = true
|
resizable = true
|
||||||
title = "Twosomnia"
|
title = "Yaak"
|
||||||
width = 1_400
|
width = 1_400
|
||||||
titleBarStyle = "Overlay"
|
titleBarStyle = "Overlay"
|
||||||
hiddenTitle = true
|
hiddenTitle = true
|
||||||
|
|||||||
@@ -27,9 +27,10 @@
|
|||||||
|
|
||||||
.cm-editor .placeholder-widget {
|
.cm-editor .placeholder-widget {
|
||||||
background-color: hsl(var(--color-blue-400));
|
background-color: hsl(var(--color-blue-400));
|
||||||
|
text-shadow: 0 0 0.2em black;
|
||||||
padding: 0.05em 0.3em;
|
padding: 0.05em 0.3em;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
color: white;
|
color: hsl(var(--color-blue-900));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,15 +56,13 @@
|
|||||||
padding-right: 1.5em;
|
padding-right: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-singleline .cm-scroller {
|
.cm-singleline .cm-editor .cm-scroller {
|
||||||
display: flex;
|
@apply flex;
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .cm-gutters {
|
.cm-editor .cm-gutters {
|
||||||
background-color: hsl(var(--color-gray-50));
|
@apply bg-gray-50 border-r-0 text-gray-200;
|
||||||
border-right: 0;
|
|
||||||
color: hsl(var(--color-gray-200));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .cm-gutterElement {
|
.cm-editor .cm-gutterElement {
|
||||||
@@ -71,83 +70,92 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .fold-gutter-icon {
|
.cm-editor .fold-gutter-icon {
|
||||||
height: 1.5em;
|
@apply pt-[0.3em] pl-[0.4em] px-[0.4em] h-4 cursor-pointer rounded;
|
||||||
padding-top: 0.2em;
|
|
||||||
padding-left: 0.4em;
|
|
||||||
padding-right: 0.4em;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 0.2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .fold-gutter-icon::after {
|
.cm-editor .fold-gutter-icon::after {
|
||||||
display: block;
|
@apply block w-1.5 h-1.5 border-transparent -rotate-45
|
||||||
width: 0.5em;
|
border-l border-b border-l-[currentColor] border-b-[currentColor] content-[''];
|
||||||
height: 0.5em;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-left-color: currentColor;
|
|
||||||
border-bottom-color: currentColor;
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
content: "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .fold-gutter-icon[data-open] {
|
.cm-editor .fold-gutter-icon[data-open] {
|
||||||
padding-top: 0.4em;
|
@apply pt-[0.4em] pl-[0.3em];
|
||||||
padding-left: 0.2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .fold-gutter-icon[data-open]::after {
|
.cm-editor .fold-gutter-icon[data-open]::after {
|
||||||
transform: rotate(-135deg);
|
@apply rotate-[-135deg];
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .fold-gutter-icon:hover {
|
.cm-editor .fold-gutter-icon:hover {
|
||||||
background-color: hsl(var(--color-gray-100)/0.2);
|
@apply text-gray-400 bg-gray-100/20;
|
||||||
color: hsl(var(--color-gray-400));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor.cm-focused .cm-gutters {
|
.cm-editor.cm-focused .cm-gutters {
|
||||||
color: hsl(var(--color-gray-300));
|
@apply text-gray-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .cm-foldPlaceholder {
|
.cm-editor .cm-foldPlaceholder {
|
||||||
background-color: hsl(var(--color-gray-100));
|
@apply px-2 border border-gray-200 bg-gray-100;
|
||||||
border: 1px solid hsl(var(--color-gray-200));
|
|
||||||
padding: 0 0.3em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .cm-activeLineGutter,
|
.cm-editor .cm-activeLineGutter,
|
||||||
.cm-editor .cm-activeLine {
|
.cm-editor .cm-activeLine {
|
||||||
background-color: transparent;
|
@apply bg-transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor.cm-focused .cm-activeLineGutter {
|
.cm-editor.cm-focused .cm-activeLineGutter {
|
||||||
color: hsl(var(--color-gray-800));
|
@apply text-gray-800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor * {
|
.cm-editor * {
|
||||||
cursor: text;
|
@apply cursor-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .cm-cursor {
|
.cm-editor .cm-cursor {
|
||||||
border-left: 2px solid hsl(var(--color-gray-900));
|
@apply border-l-2 border-gray-800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .cm-selectionBackground {
|
.cm-editor .cm-selectionBackground {
|
||||||
background-color: hsl(var(--color-gray-200));
|
@apply bg-gray-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor.cm-focused .cm-selectionBackground {
|
.cm-editor.cm-focused .cm-selectionBackground {
|
||||||
background-color: hsl(var(--color-gray-200));
|
@apply bg-gray-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --> Add padding to container. For some reason, using padding on both adds an extra
|
/* --> Add padding to container. For some reason, using padding on both adds an extra
|
||||||
* 1px offset so we need to use a combination of padding and margin.
|
* 1px offset so we need to use a combination of padding and margin.
|
||||||
*/
|
*/
|
||||||
.cm-editor .cm-gutters {
|
.cm-editor .cm-gutters {
|
||||||
padding-top: 0.2em;
|
@apply pt-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .cm-content {
|
.cm-editor .cm-content {
|
||||||
margin-top: 0.2em;
|
@apply mt-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* <-- */
|
/* <-- */
|
||||||
|
|
||||||
|
.cm-editor .cm-tooltip {
|
||||||
|
@apply shadow-lg border-0 bg-background rounded overflow-hidden text-gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-editor .cm-tooltip * {
|
||||||
|
@apply transition-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-editor .cm-tooltip.cm-tooltip-autocomplete > ul {
|
||||||
|
@apply p-1 max-h-[40vh];
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-editor .cm-tooltip.cm-tooltip-autocomplete > ul > li {
|
||||||
|
@apply cursor-default py-1 px-2 rounded-sm text-gray-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-editor .cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] {
|
||||||
|
@apply bg-gray-50 text-gray-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-editor .cm-tooltip.cm-tooltip-autocomplete .cm-completionIcon {
|
||||||
|
@apply text-sm;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import type { Transaction, TransactionSpec } from '@codemirror/state';
|
import { defaultKeymap } from '@codemirror/commands';
|
||||||
import { Compartment, EditorSelection, EditorState, Prec } from '@codemirror/state';
|
import { Compartment, EditorState, Prec } from '@codemirror/state';
|
||||||
import { placeholder as placeholderExt } from '@codemirror/view';
|
import { keymap, placeholder as placeholderExt } from '@codemirror/view';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { EditorView } from 'codemirror';
|
import { EditorView } from 'codemirror';
|
||||||
import type { HTMLAttributes } from 'react';
|
import type { HTMLAttributes } from 'react';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import './Editor.css';
|
import './Editor.css';
|
||||||
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
||||||
|
import { singleLineExt } from './singleLine';
|
||||||
|
|
||||||
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
||||||
contentType: string;
|
contentType: string;
|
||||||
@@ -106,43 +107,25 @@ function getExtensions({
|
|||||||
>) {
|
>) {
|
||||||
const ext = getLanguageExtension({ contentType, useTemplating });
|
const ext = getLanguageExtension({ contentType, useTemplating });
|
||||||
return [
|
return [
|
||||||
...(singleLine
|
|
||||||
? [
|
|
||||||
Prec.high(
|
|
||||||
EditorView.domEventHandlers({
|
|
||||||
keydown: (e) => {
|
|
||||||
// TODO: Figure out how to not have this not trigger on autocomplete selection
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
e.preventDefault();
|
|
||||||
onSubmit?.();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
EditorState.transactionFilter.of(
|
|
||||||
(tr: Transaction): TransactionSpec | TransactionSpec[] => {
|
|
||||||
if (!tr.isUserEvent('input.paste')) return tr;
|
|
||||||
|
|
||||||
const trs: TransactionSpec[] = [];
|
|
||||||
tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
|
||||||
let insert = '';
|
|
||||||
for (const line of inserted) {
|
|
||||||
insert += line.replace('\n', '');
|
|
||||||
}
|
|
||||||
const changes = [{ from: fromB, to: toA, insert }];
|
|
||||||
// Update selection now that the text has been changed
|
|
||||||
const selection = EditorSelection.create([EditorSelection.cursor(toB - 1)], 0);
|
|
||||||
trs.push({ ...tr, selection, changes });
|
|
||||||
});
|
|
||||||
return trs;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...baseExtensions,
|
...baseExtensions,
|
||||||
|
keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== 'Enter') : defaultKeymap),
|
||||||
|
...(singleLine ? [singleLineExt()] : []),
|
||||||
...(!singleLine ? [multiLineExtensions] : []),
|
...(!singleLine ? [multiLineExtensions] : []),
|
||||||
...(ext ? [ext] : []),
|
...(ext ? [ext] : []),
|
||||||
...(placeholder ? [placeholderExt(placeholder)] : []),
|
...(placeholder ? [placeholderExt(placeholder)] : []),
|
||||||
|
|
||||||
|
// Handle onSubmit
|
||||||
|
...(onSubmit
|
||||||
|
? [
|
||||||
|
EditorView.domEventHandlers({
|
||||||
|
keydown: (e) => {
|
||||||
|
console.log('KEYDOWN', e);
|
||||||
|
if (e.key === 'Enter') onSubmit?.();
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
// Handle onChange
|
||||||
EditorView.updateListener.of((update) => {
|
EditorView.updateListener.of((update) => {
|
||||||
if (typeof onChange === 'function' && update.docChanged) {
|
if (typeof onChange === 'function' && update.docChanged) {
|
||||||
onChange(update.state.doc.toString());
|
onChange(update.state.doc.toString());
|
||||||
|
|||||||
@@ -8,25 +8,30 @@ const variables = [
|
|||||||
{ name: 'BASE_URL' },
|
{ name: 'BASE_URL' },
|
||||||
{ name: 'TOKEN' },
|
{ name: 'TOKEN' },
|
||||||
{ name: 'PROJECT_ID' },
|
{ name: 'PROJECT_ID' },
|
||||||
|
{ name: 'DUMMY' },
|
||||||
|
{ name: 'DUMMY_2' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function myCompletions(context: CompletionContext) {
|
export function myCompletions(context: CompletionContext) {
|
||||||
const toStartOfName = context.matchBefore(/\w*/);
|
const toStartOfName = context.explicit ? context.matchBefore(/\w*/) : context.matchBefore(/\w+/);
|
||||||
const toStartOfVariable = context.matchBefore(/\$\{.*/);
|
const toStartOfVariable = context.matchBefore(/\$\{?\[?\s*\w*/);
|
||||||
const toMatch = toStartOfVariable ?? toStartOfName ?? null;
|
const toMatch = toStartOfVariable ?? toStartOfName ?? null;
|
||||||
|
|
||||||
if (toMatch === null) {
|
if (toMatch === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toMatch.from === toMatch.to && !context.explicit) {
|
// Match a minimum of two characters when typing a variable ${[...]} to prevent it
|
||||||
|
// from opening on "$"
|
||||||
|
if (toStartOfVariable !== null && toMatch.to - toMatch.from < 2 && !context.explicit) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
from: toMatch.from,
|
from: toMatch.from,
|
||||||
options: variables.map((v) => ({
|
options: variables.map((v) => ({
|
||||||
label: `${openTag}${v.name}${closeTag}`,
|
label: toStartOfVariable ? `${openTag}${v.name}${closeTag}` : v.name,
|
||||||
|
apply: `${openTag}${v.name}${closeTag}`,
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -102,13 +102,12 @@ export function getLanguageExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const baseExtensions = [
|
export const baseExtensions = [
|
||||||
keymap.of([...defaultKeymap]),
|
|
||||||
highlightSpecialChars(),
|
highlightSpecialChars(),
|
||||||
history(),
|
history(),
|
||||||
drawSelection(),
|
drawSelection(),
|
||||||
dropCursor(),
|
dropCursor(),
|
||||||
bracketMatching(),
|
bracketMatching(),
|
||||||
autocompletion(),
|
autocompletion({ activateOnTyping: false, closeOnBlur: true }),
|
||||||
syntaxHighlighting(myHighlightStyle),
|
syntaxHighlighting(myHighlightStyle),
|
||||||
EditorState.allowMultipleSelections.of(true),
|
EditorState.allowMultipleSelections.of(true),
|
||||||
];
|
];
|
||||||
|
|||||||
29
src-web/components/Editor/singleLine.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import type { Transaction, TransactionSpec } from '@codemirror/state';
|
||||||
|
import { EditorSelection, EditorState } from '@codemirror/state';
|
||||||
|
|
||||||
|
export function singleLineExt() {
|
||||||
|
return EditorState.transactionFilter.of(
|
||||||
|
(tr: Transaction): TransactionSpec | TransactionSpec[] => {
|
||||||
|
if (!tr.isUserEvent('input')) return tr;
|
||||||
|
|
||||||
|
const trs: TransactionSpec[] = [];
|
||||||
|
tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
||||||
|
let insert = '';
|
||||||
|
let newlinesRemoved = 0;
|
||||||
|
for (const line of inserted) {
|
||||||
|
const newLine = line.replace('\n', '');
|
||||||
|
newlinesRemoved += line.length - newLine.length;
|
||||||
|
insert += newLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update cursor position based on how many newlines were removed
|
||||||
|
const cursor = EditorSelection.cursor(toB - newlinesRemoved);
|
||||||
|
const selection = EditorSelection.create([cursor], 0);
|
||||||
|
|
||||||
|
const changes = [{ from: fromB, to: toA, insert }];
|
||||||
|
trs.push({ ...tr, selection, changes });
|
||||||
|
});
|
||||||
|
return trs;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
import { LRLanguage, LanguageSupport } from '@codemirror/language';
|
import { LanguageSupport, LRLanguage } from '@codemirror/language';
|
||||||
import { parseMixed } from '@lezer/common';
|
import { parseMixed } from '@lezer/common';
|
||||||
import { myCompletions } from '../completion/completion';
|
import { myCompletions } from '../completion/completion';
|
||||||
import { placeholders } from '../widgets';
|
import { placeholders } from '../widgets';
|
||||||
import { parser as twigParser } from './twig';
|
import { parser as twigParser } from './twig';
|
||||||
|
|
||||||
export function twig(base?: LanguageSupport) {
|
export function twig(base?: LanguageSupport) {
|
||||||
const parser = mixedOrPlainParser(base);
|
const language = mixedOrPlainLanguage(base);
|
||||||
const twigLanguage = LRLanguage.define({ name: 'twig', parser, languageData: {} });
|
const completion = language.data.of({
|
||||||
const completion = twigLanguage.data.of({
|
|
||||||
autocomplete: myCompletions,
|
autocomplete: myCompletions,
|
||||||
});
|
});
|
||||||
const languageSupport = new LanguageSupport(twigLanguage, [completion]);
|
const languageSupport = new LanguageSupport(language, [completion]);
|
||||||
|
|
||||||
if (base) {
|
if (base) {
|
||||||
const completion2 = base.language.data.of({ autocomplete: myCompletions });
|
const completion2 = base.language.data.of({ autocomplete: myCompletions });
|
||||||
@@ -21,31 +20,22 @@ export function twig(base?: LanguageSupport) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mixedOrPlainParser(base?: LanguageSupport) {
|
function mixedOrPlainLanguage(base?: LanguageSupport): LRLanguage {
|
||||||
if (base === undefined) {
|
const name = 'twig';
|
||||||
return twigParser;
|
|
||||||
|
if (base == null) {
|
||||||
|
return LRLanguage.define({ name, parser: twigParser });
|
||||||
}
|
}
|
||||||
|
|
||||||
const mixedParser = twigParser.configure({
|
const parser = twigParser.configure({
|
||||||
props: [
|
|
||||||
// Add basic folding/indent metadata
|
|
||||||
// foldNodeProp.add({ Conditional: foldInside }),
|
|
||||||
// indentNodeProp.add({
|
|
||||||
// Conditional: (cx) => {
|
|
||||||
// const closed = /^\s*\{% endif/.test(cx.textAfter);
|
|
||||||
// return cx.lineIndent(cx.node.from) + (closed ? 0 : cx.unit);
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
],
|
|
||||||
wrap: parseMixed((node) => {
|
wrap: parseMixed((node) => {
|
||||||
return node.type.isTop
|
if (!node.type.isTop) return null;
|
||||||
? {
|
return {
|
||||||
parser: base.language.parser,
|
parser: base.language.parser,
|
||||||
overlay: (node) => node.type.name === 'Text',
|
overlay: (node) => node.type.name === 'Text',
|
||||||
}
|
};
|
||||||
: null;
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return mixedParser;
|
return LRLanguage.define({ name, parser });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ directive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@tokens {
|
@tokens {
|
||||||
Text { ![${[] Text? }
|
Text { ![$] Text? }
|
||||||
space { @whitespace+ }
|
space { @whitespace+ }
|
||||||
DirectiveContent { ![\]}$] DirectiveContent? }
|
DirectiveContent { ![\]}] DirectiveContent? }
|
||||||
@precedence { space DirectiveContent }
|
@precedence { space DirectiveContent }
|
||||||
"${[" "]}"
|
"${[" "]}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const parser = LRParser.deserialize({
|
|||||||
propSources: [highlight],
|
propSources: [highlight],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 1,
|
repeatNodeCount: 1,
|
||||||
tokenData: ")a~RqOX#YX^%i^p#Ypq%iqt#Ytu'vu!}#Y!}#O$V#O#P#Y#P#Q(X#Q#o#Y#o#p$V#p#q#Y#q#r$t#r#y#Y#y#z%i#z$f#Y$f$g%i$g#BY#Y#BY#BZ%i#BZ$IS#Y$IS$I_%i$I_$I|#Y$I|$JO%i$JO$JT#Y$JT$JU%i$JU$KV#Y$KV$KW%i$KW&FU#Y&FU&FV%i&FV;'S#Y;'S;=`%c<%lO#YR#a[UPSQOt#Yu!}#Y!}#O$V#O#P#Y#P#Q$t#Q#o#Y#o#p$V#p#q#Y#q#r$t#r;'S#Y;'S;=`%c<%lO#YQ$[USQOt$Vu#P$V#Q#q$V#r;'S$V;'S;=`$n<%lO$VQ$qP;=`<%l$VP$yUUPOt$tu!}$t#O#o$t#p;'S$t;'S;=`%]<%lO$tP%`P;=`<%l$tR%fP;=`<%l#YR%rpUPYQSQOX#YX^%i^p#Ypq%iqt#Yu!}#Y!}#O$V#O#P#Y#P#Q$t#Q#o#Y#o#p$V#p#q#Y#q#r$t#r#y#Y#y#z%i#z$f#Y$f$g%i$g#BY#Y#BY#BZ%i#BZ$IS#Y$IS$I_%i$I_$I|#Y$I|$JO%i$JO$JT#Y$JT$JU%i$JU$KV#Y$KV$KW%i$KW&FU#Y&FU&FV%i&FV;'S#Y;'S;=`%c<%lO#Y~'yP#o#p'|~(PP!}#O(S~(XOR~R(^WUPOt$tu!}$t#O#o$t#p#q$t#q#r(v#r;'S$t;'S;=`%]<%lO$tR(}UTQUPOt$tu!}$t#O#o$t#p;'S$t;'S;=`%]<%lO$t",
|
tokenData: ")gRRmOX!|X^$y^p!|pq$yqt!|tu&}u#P!|#P#Q(k#Q#q!|#q#r$[#r#y!|#y#z$y#z$f!|$f$g$y$g#BY!|#BY#BZ$y#BZ$IS!|$IS$I_$y$I_$I|!|$I|$JO$y$JO$JT!|$JT$JU$y$JU$KV!|$KV$KW$y$KW&FU!|&FU&FV$y&FV;'S!|;'S;=`$s<%lO!|R#TXUPSQOt!|tu#pu#P!|#P#Q$[#Q#q!|#q#r$[#r;'S!|;'S;=`$s<%lO!|Q#uTSQO#P#p#Q#q#p#r;'S#p;'S;=`$U<%lO#pQ$XP;=`<%l#pP$aSUPOt$[u;'S$[;'S;=`$m<%lO$[P$pP;=`<%l$[R$vP;=`<%l!|R%SmUPYQSQOX!|X^$y^p!|pq$yqt!|tu#pu#P!|#P#Q$[#Q#q!|#q#r$[#r#y!|#y#z$y#z$f!|$f$g$y$g#BY!|#BY#BZ$y#BZ$IS!|$IS$I_$y$I_$I|!|$I|$JO$y$JO$JT!|$JT$JU$y$JU$KV!|$KV$KW$y$KW&FU!|&FU&FV$y&FV;'S!|;'S;=`$s<%lO!|R'SVSQO#P#p#Q#o#p#o#p'i#p#q#p#r;'S#p;'S;=`$U<%lO#pR'nVSQO!}#p!}#O(T#O#P#p#Q#q#p#r;'S#p;'S;=`$U<%lO#pR([TRPSQO#P#p#Q#q#p#r;'S#p;'S;=`$U<%lO#pR(pUUPOt$[u#q$[#q#r)S#r;'S$[;'S;=`$m<%lO$[R)ZSTQUPOt$[u;'S$[;'S;=`$m<%lO$[",
|
||||||
tokenizers: [0, 1],
|
tokenizers: [0, 1],
|
||||||
topRules: {"Template":[0,1]},
|
topRules: {"Template":[0,1]},
|
||||||
tokenPrec: 25
|
tokenPrec: 25
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export function ResponsePane({ requestId, error }: Props) {
|
|||||||
},
|
},
|
||||||
'-----',
|
'-----',
|
||||||
...responses.data.slice(0, 10).map((r) => ({
|
...responses.data.slice(0, 10).map((r) => ({
|
||||||
label: r.status + ' - ' + r.elapsed,
|
label: r.status + ' - ' + r.elapsed + ' ms',
|
||||||
leftSlot: response?.id === r.id ? <Icon icon="check" /> : <></>,
|
leftSlot: response?.id === r.id ? <Icon icon="check" /> : <></>,
|
||||||
onSelect: () => setActiveResponseId(r.id),
|
onSelect: () => setActiveResponseId(r.id),
|
||||||
})),
|
})),
|
||||||
|
|||||||