Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vml Library Support #1602

Merged
merged 47 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fc7171a
grammar updates to allow date ranges and validate issue_dynamic
joswig Jan 23, 2025
2e5ea75
checks that spawned block names exist
joswig Jan 29, 2025
833130f
wired up issue_dynamic form editor
joswig Jan 29, 2025
4757f80
snippets for some block structures
joswig Jan 30, 2025
fb1ad56
validate issue_dynamic arguments
joswig Jan 30, 2025
c8bcae6
support SPACECRAFT_TIME() command and encoded_string arg
joswig Feb 1, 2025
3f8b95e
tooltip for encoded string
joswig Feb 2, 2025
cbe240d
st warning
joswig Feb 2, 2025
ef8a0fe
extract functions for spawn and issue validation
joswig Feb 2, 2025
dd98917
display byte array in selected command
joswig Feb 2, 2025
5ec048b
show decoded by array as read only in form view
joswig Feb 3, 2025
67e492c
form view for library sequence
joswig Feb 3, 2025
e50d8c8
tooltips for spawn and issue_dynamic args
joswig Feb 4, 2025
7e43aa0
remove redundant display of command description
joswig Feb 4, 2025
f7df7f9
check variable and global reference in issue*
joswig Feb 5, 2025
9bd2763
remove unused parameter
joswig Feb 5, 2025
294802e
svelte-check version doesn't support set union
joswig Feb 5, 2025
4fd6150
button to show error panel
joswig Feb 5, 2025
7d0dbad
polish on completion suggestions
joswig Feb 5, 2025
ff87f07
add default arg for globals
joswig Feb 5, 2025
946224d
reduce nesting
joswig Feb 5, 2025
b71cda7
widen allowed input type on guard
joswig Feb 5, 2025
a5ddaec
remove obosolete comment
joswig Feb 5, 2025
134d217
exempt variable declarations from checking if in scope
joswig Feb 5, 2025
ab159fa
spelling
joswig Feb 6, 2025
03b1cae
expand docs on issue vs issue_dynamic
joswig Feb 6, 2025
8aabb7e
split up numerci cases
joswig Feb 6, 2025
7b6c973
readability of content assist
joswig Feb 6, 2025
d71c560
stricter types
joswig Feb 6, 2025
e11e454
readability of completion code
joswig Feb 7, 2025
a46b0f2
add comment to make SeqN adaptation symmetric
joswig Feb 7, 2025
f1f6086
reduce conditional nesting
joswig Feb 7, 2025
b102f8b
fix svelte-check lint
joswig Feb 7, 2025
74a73f9
remove extraneous truthyness check
joswig Feb 7, 2025
ef5fe65
reduce nesting
joswig Feb 7, 2025
f9348ab
reduce nesting
joswig Feb 7, 2025
72e2a05
Define constant for language name
joswig Feb 7, 2025
578c28b
remove try/catch around svelte component creation
joswig Feb 7, 2025
6543a6f
enum for sequence types
joswig Feb 7, 2025
e7929d3
wrap form inputs in div for byte editor
joswig Feb 7, 2025
919dca4
helper for computing library sequences for seqn
joswig Feb 7, 2025
ebf874e
missed file in prior commit
joswig Feb 7, 2025
451932c
smaller pieces for auto-complete
joswig Feb 9, 2025
cfea843
better handling of typing new issue, issue_dynamic, etc
joswig Feb 10, 2025
69a9cb0
minor cleanup
joswig Feb 11, 2025
2331c08
Move SequenceTypes to enums file
joswig Feb 11, 2025
cdabbfb
accept sonarqube false positive
joswig Feb 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import type { ArgTextDef, TimeTagInfo } from '../../../types/sequencing';
import type { CommandInfoMapper } from '../../../utilities/codemirror/commandInfoMapper';
import { tooltip } from '../../../utilities/tooltip';
import Collapse from '../../Collapse.svelte';
import AddMissingArgsButton from '../form/AddMissingArgsButton.svelte';
import ArgEditor from '../form/ArgEditor.svelte';
import StringEditor from '../form/StringEditor.svelte';
Expand Down Expand Up @@ -121,10 +120,6 @@
{#if !!commandNode}
{#if commandInfoMapper.nodeTypeHasArguments(commandNode)}
{#if !!commandDef}
<fieldset>
<Collapse headerHeight={24} title={commandDef.stem} padContent={false}>{commandDef.description}</Collapse>
</fieldset>

{#each editorArgInfoArray as argInfo}
<ArgEditor
{argInfo}
Expand Down
103 changes: 69 additions & 34 deletions src/components/sequencing/SequenceEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<script lang="ts">
import { json } from '@codemirror/lang-json';
import { indentService, syntaxTree } from '@codemirror/language';
import { lintGutter } from '@codemirror/lint';
import { lintGutter, openLintPanel } from '@codemirror/lint';
import { Compartment, EditorState } from '@codemirror/state';
import { type ViewUpdate } from '@codemirror/view';
import type { SyntaxNode, Tree } from '@lezer/common';
Expand All @@ -20,7 +20,7 @@
import ExpandIcon from 'bootstrap-icons/icons/arrow-bar-up.svg?component';
import ClipboardIcon from 'bootstrap-icons/icons/clipboard.svg?component';
import DownloadIcon from 'bootstrap-icons/icons/download.svg?component';
import { EditorView, basicSetup } from 'codemirror';
import { basicSetup, EditorView } from 'codemirror';
import { debounce } from 'lodash-es';
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import { TOKEN_ERROR } from '../../constants/seq-n-grammar-constants';
Expand All @@ -43,27 +43,29 @@
userSequences,
} from '../../stores/sequencing';
import type { User } from '../../types/app';
import type {
ArgTextDef,
IOutputFormat,
ISequenceAdaptation,
LibrarySequence,
Parcel,
TimeTagInfo,
import {
type ArgTextDef,
type IOutputFormat,
type ISequenceAdaptation,
type LibrarySequence,
type LibrarySequenceMap,
type Parcel,
type TimeTagInfo,
} from '../../types/sequencing';
import { SeqLanguage, setupLanguageSupport } from '../../utilities/codemirror';
import { isFswCommandArgumentRepeat } from '../../utilities/codemirror/codemirror-utils';
import { setupLanguageSupport } from '../../utilities/codemirror';
import { isFswCommandArgumentRepeat, unquoteUnescape } from '../../utilities/codemirror/codemirror-utils';
import type { CommandInfoMapper } from '../../utilities/codemirror/commandInfoMapper';
import { seqNHighlightBlock, seqqNBlockHighlighter } from '../../utilities/codemirror/seq-n-highlighter';
import { SeqNCommandInfoMapper } from '../../utilities/codemirror/seq-n-tree-utils';
import { SeqNCommandInfoMapper, userSequenceToLibrarySequence } from '../../utilities/codemirror/seq-n-tree-utils';
import { blockTheme } from '../../utilities/codemirror/themes/block';
import {
setupVmlLanguageSupport,
vmlAdaptation,
vmlBlockHighlighter,
vmlHighlightBlock,
} from '../../utilities/codemirror/vml/vml';
import { vmlAutoComplete } from '../../utilities/codemirror/vml/vmlAdaptation';
import { parseFunctionSignatures, vmlAutoComplete } from '../../utilities/codemirror/vml/vmlAdaptation';
import { librarySequenceToFswCommand } from '../../utilities/codemirror/vml/vmlBlockLibrary';
import { vmlFormat } from '../../utilities/codemirror/vml/vmlFormatter';
import { vmlLinter } from '../../utilities/codemirror/vml/vmlLinter';
import { vmlTooltip } from '../../utilities/codemirror/vml/vmlTooltip';
Expand All @@ -73,7 +75,6 @@
import { getCustomArgDef, inputLinter, outputLinter } from '../../utilities/sequence-editor/extension-points';
import { seqNFormat } from '../../utilities/sequence-editor/sequence-autoindent';
import { sequenceTooltip } from '../../utilities/sequence-editor/sequence-tooltip';
import { parseVariables } from '../../utilities/sequence-editor/to-seq-json';
import { showFailureToast, showSuccessToast } from '../../utilities/toast';
import { tooltip } from '../../utilities/tooltip';
import Menu from '../menus/Menu.svelte';
Expand Down Expand Up @@ -101,8 +102,8 @@
const debouncedSeqNHighlightBlock = debounce(seqNHighlightBlock, 250);
const debouncedVmlHighlightBlock = debounce(vmlHighlightBlock, 250);

let clientHeightGridRightBottom: number;
let clientHeightGridRightTop: number;
let clientHeightGridRightBottom: number = 0;
let clientHeightGridRightTop: number = 0;
let compartmentSeqJsonLinter: Compartment;
let compartmentSeqLanguage: Compartment;
let compartmentSeqLinter: Compartment;
Expand All @@ -113,6 +114,7 @@
let commandDictionary: CommandDictionary | null;
let disableCopyAndExport: boolean = true;
let parameterDictionaries: ParameterDictionary[] = [];
let librarySequenceMap: LibrarySequenceMap = {};
let librarySequences: LibrarySequence[] = [];
let commandFormBuilderGrid: string;
let editorOutputDiv: HTMLDivElement;
Expand Down Expand Up @@ -192,30 +194,36 @@
}
});

librarySequences = $userSequences
.filter(sequence => sequence.workspace_id === workspaceId && sequence.name !== sequenceName)
.map(sequence => {
const tree = SeqLanguage.parser.parse(sequence.definition);
return {
name: sequence.name,
parameters: parseVariables(tree.topNode, sequence.definition, 'ParameterDeclaration') ?? [],
tree,
workspace_id: sequence.workspace_id,
};
});
if (isInVmlMode) {
librarySequences = $userSequences
.filter(sequence => sequence.workspace_id === workspaceId)
.flatMap(sequence => parseFunctionSignatures(sequence.definition, sequence.workspace_id));
} else {
librarySequences = $userSequences
.filter(sequence => sequence.workspace_id === workspaceId && sequence.name !== sequenceName)
.map(userSequenceToLibrarySequence);
}

librarySequenceMap = Object.fromEntries(librarySequences.map(seq => [seq.name, seq]));

if (unparsedCommandDictionary) {
if (sequenceName && isInVmlMode) {
getParsedCommandDictionary(unparsedCommandDictionary, user).then(parsedCommandDictionary => {
commandDictionary = parsedCommandDictionary;
editorSequenceView.dispatch({
effects: compartmentSeqLanguage.reconfigure(setupVmlLanguageSupport(vmlAutoComplete(commandDictionary))),
effects: compartmentSeqLanguage.reconfigure(
setupVmlLanguageSupport(
vmlAutoComplete(commandDictionary, $sequenceAdaptation.globals ?? [], librarySequenceMap),
),
),
});
editorSequenceView.dispatch({
effects: compartmentSeqLinter.reconfigure(vmlLinter(commandDictionary)),
effects: compartmentSeqLinter.reconfigure(
vmlLinter(commandDictionary, librarySequenceMap, $sequenceAdaptation.globals ?? []),
),
});
editorSequenceView.dispatch({
effects: compartmentSeqTooltip.reconfigure(vmlTooltip(commandDictionary)),
effects: compartmentSeqTooltip.reconfigure(vmlTooltip(commandDictionary, librarySequenceMap)),
});
});
} else {
Expand All @@ -237,6 +245,7 @@
// Reconfigure sequence editor.
editorSequenceView.dispatch({
effects: [
// TODO: use librarySequenceMap here, requires a change to adaptations so defer until changing adaptation API
compartmentSeqLanguage.reconfigure(
setupLanguageSupport(
$sequenceAdaptation.autoComplete(
Expand Down Expand Up @@ -288,8 +297,9 @@

$: commandNode = commandInfoMapper.getContainingCommand(selectedNode);
$: commandNameNode = commandInfoMapper.getNameNode(commandNode);
$: commandName = commandNameNode && editorSequenceView.state.sliceDoc(commandNameNode.from, commandNameNode.to);
$: commandDef = getCommandDef(commandDictionary, commandName ?? '');
$: commandName =
commandNameNode && unquoteUnescape(editorSequenceView.state.sliceDoc(commandNameNode.from, commandNameNode.to));
$: commandDef = getCommandDef(commandDictionary, librarySequenceMap, commandName ?? '');
$: timeTagNode = getTimeTagInfo(editorSequenceView, commandNode);
$: argInfoArray = getArgumentInfo(
commandInfoMapper,
Expand Down Expand Up @@ -473,6 +483,10 @@
toggleSeqJsonPreview = !toggleSeqJsonPreview;
}

function showErrorPanel() {
openLintPanel(editorSequenceView);
}

function formatDocument() {
if (isInVmlMode) {
vmlFormat(editorSequenceView);
Expand All @@ -496,8 +510,21 @@
);
}

function getCommandDef(commandDictionary: CommandDictionary | null, stemName: string): FswCommand | null {
return commandDictionary?.fswCommandMap[stemName] ?? null;
function getCommandDef(
commandDictionary: CommandDictionary | null,
librarySequenceMap: LibrarySequenceMap,
stemName: string,
): FswCommand | null {
const commandDefFromCommandDictionary = commandDictionary?.fswCommandMap[stemName];
if (commandDefFromCommandDictionary) {
return commandDefFromCommandDictionary;
}

const librarySeqDef = librarySequenceMap[stemName];
if (librarySeqDef) {
return librarySequenceToFswCommand(librarySeqDef);
}
return null;
}

function getVariablesInScope(
Expand Down Expand Up @@ -600,6 +627,14 @@
<SectionTitle>{title}</SectionTitle>

<div class="right">
<button
use:tooltip={{ content: 'Show Error Panel', placement: 'top' }}
class="st-button icon-button secondary ellipsis"
on:click={showErrorPanel}
>
Error Panel
</button>

<button
use:tooltip={{ content: 'Format sequence whitespace', placement: 'top' }}
class="st-button icon-button secondary ellipsis"
Expand Down
19 changes: 19 additions & 0 deletions src/components/sequencing/StringTooltip.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<svelte:options immutable={true} />

<script lang="ts">
export let message: string;
</script>

<div class="sequence-tooltip">
<div class="container">
{message}
</div>
</div>

<style>
.container {
align-items: center;
display: flex;
padding: 5px;
}
</style>
5 changes: 4 additions & 1 deletion src/components/sequencing/form/ArgEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import AddMissingArgsButton from './AddMissingArgsButton.svelte';
import ArgTitle from './ArgTitle.svelte';
import BooleanEditor from './BooleanEditor.svelte';
import ByteArrayEditor from './ByteArrayEditor.svelte';
import EnumEditor from './EnumEditor.svelte';
import ExtraArgumentEditor from './ExtraArgumentEditor.svelte';
import NumEditor from './NumEditor.svelte';
Expand Down Expand Up @@ -92,7 +93,9 @@
}}
/>
{/if}
{#if isSymbol && isFswCommandArgumentEnum(argDef)}
{#if argInfo.node && commandInfoMapper.isByteArrayArg(argInfo.node)}
<ByteArrayEditor value={argInfo.text ?? ''} argNode={argInfo.node} {commandInfoMapper} />
{:else if isSymbol && isFswCommandArgumentEnum(argDef)}
<div class="st-typography-small-caps">Reference</div>
<EnumEditor
{argDef}
Expand Down
25 changes: 25 additions & 0 deletions src/components/sequencing/form/ByteArrayEditor.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<svelte:options immutable={true} />

<script lang="ts">
import type { SyntaxNode } from '@lezer/common';
import { decodeInt32Array } from '../../../utilities/codemirror/codemirror-utils';
import type { CommandInfoMapper } from '../../../utilities/codemirror/commandInfoMapper';

export let argNode: SyntaxNode;
export let commandInfoMapper: CommandInfoMapper;
export let value: string;

let decodedValue: string;

$: {
const arrayNodes = commandInfoMapper.getByteArrayElements && commandInfoMapper.getByteArrayElements(argNode, value);
if (arrayNodes) {
decodedValue = decodeInt32Array(arrayNodes);
}
}
</script>

<div>
<input class="st-input w-100" spellcheck="false" bind:value title="encoded string" disabled={true} />
<input class="st-input w-100" spellcheck="false" bind:value={decodedValue} title="decoded string" disabled={true} />
</div>
4 changes: 4 additions & 0 deletions src/enums/sequencing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum SequenceTypes {
LIBRARY = 'library',
USER = 'user',
}
4 changes: 4 additions & 0 deletions src/types/sequencing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
import type { VariableDeclaration } from '@nasa-jpl/seq-json-schema/types';
import type { EditorView } from 'codemirror';
import type { DictionaryTypes } from '../enums/dictionaryTypes';
import type { SequenceTypes } from '../enums/sequencing';
import type { ArgDelegator } from '../utilities/sequence-editor/extension-points';
import type { UserId } from './app';
import type { GlobalType } from './global-type';
Expand Down Expand Up @@ -155,9 +156,12 @@ export type LibrarySequence = {
name: string;
parameters: VariableDeclaration[];
tree: Tree;
type: SequenceTypes.LIBRARY;
workspace_id: number;
};

export type LibrarySequenceMap = { [sequenceName: string]: LibrarySequence };

export type UserSequenceInsertInput = Omit<UserSequence, 'created_at' | 'id' | 'owner' | 'updated_at'>;

export type Workspace = {
Expand Down
2 changes: 1 addition & 1 deletion src/utilities/codemirror/codemirror-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe('isHexValue', () => {
});
});

describe('Command and argument typeguards', () => {
describe('Command and argument type guards', () => {
test('isFswCommand', () => {
expect(
isFswCommand({
Expand Down
18 changes: 18 additions & 0 deletions src/utilities/codemirror/codemirror-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type { CommandInfoMapper } from './commandInfoMapper';
export function isFswCommand(command: FswCommand | HwCommand): command is FswCommand {
return (command as FswCommand).type === 'fsw_command';
}

export function isHwCommand(command: FswCommand | HwCommand): command is HwCommand {
return (command as HwCommand).type === 'hw_command';
}
Expand Down Expand Up @@ -181,3 +182,20 @@ export function parseNumericArg(argText: string, dictArgType: 'float' | 'integer
export function isHexValue(argText: string) {
return /^0x[\da-f]+$/i.test(argText);
}

export function decodeInt32Array(encoded: string[]) {
return encoded
.map(charAsHex => {
const n = Number(charAsHex);
return String.fromCodePoint((n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff);
})
.join('');
}

export function encodeInt32Array(s: string) {
const encoded: string[] = [];
for (let i = 0; i < s.length; i += 4) {
encoded.push(s.codePointAt(i)?.toString(16) ?? '00');
}
return encoded;
}
4 changes: 4 additions & 0 deletions src/utilities/codemirror/commandInfoMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface CommandInfoMapper {
/** collects argument nodes from sub-tree of this command argument container */
getArgumentsFromContainer(containerNode: SyntaxNode): SyntaxNode[];

getByteArrayElements?(node: SyntaxNode | null, arrayText: string): string[] | null;

/** ascends parse tree to find scope to display in form editor */
getContainingCommand(node: SyntaxNode | null): SyntaxNode | null;

Expand All @@ -34,6 +36,8 @@ export interface CommandInfoMapper {
/** is argument node a variable, false implies literal */
isArgumentNodeOfVariableType(argNode: SyntaxNode | null): boolean;

isByteArrayArg(argNode: SyntaxNode | null): boolean;

/** checks if select list should be used */
nodeTypeEnumCompatible(node: SyntaxNode | null): boolean;

Expand Down
Loading
Loading