Skip to content

Commit

Permalink
tooltips for spawn and issue_dynamic args
Browse files Browse the repository at this point in the history
  • Loading branch information
joswig committed Feb 4, 2025
1 parent 2be0db9 commit 9bcdafe
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 94 deletions.
2 changes: 1 addition & 1 deletion src/components/sequencing/SequenceEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@
effects: compartmentSeqLinter.reconfigure(vmlLinter(commandDictionary, librarySequenceMap)),
});
editorSequenceView.dispatch({
effects: compartmentSeqTooltip.reconfigure(vmlTooltip(commandDictionary)),
effects: compartmentSeqTooltip.reconfigure(vmlTooltip(commandDictionary, librarySequenceMap)),
});
});
} else {
Expand Down
145 changes: 81 additions & 64 deletions src/utilities/codemirror/vml/vmlTooltip.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { syntaxTree } from '@codemirror/language';
import type { Extension } from '@codemirror/state';
import { hoverTooltip, type Tooltip } from '@codemirror/view';
import type { SyntaxNode } from '@lezer/common';
import type {
CommandDictionary,
FswCommand,
Expand All @@ -11,23 +12,28 @@ import type { EditorView } from 'codemirror';
import ArgumentTooltip from '../../../components/sequencing/ArgumentTooltip.svelte';
import CommandTooltip from '../../../components/sequencing/CommandTooltip.svelte';
import StringTooltip from '../../../components/sequencing/StringTooltip.svelte';
import type { LibrarySequence } from '../../../types/sequencing';
import { getTokenPositionInLine } from '../../sequence-editor/sequence-tooltip';
import { checkContainment, getNearestAncestorNodeOfType } from '../../sequence-editor/tree-utils';
import { unquoteUnescape } from '../codemirror-utils';
import { librarySequenceToFswCommand } from './vmlBlockLibrary';
import {
RULE_BYTE_ARRAY,
RULE_CALL_PARAMETER,
RULE_CALL_PARAMETERS,
RULE_CONSTANT,
RULE_FUNCTION_NAME,
RULE_ISSUE,
RULE_ISSUE_DYNAMIC,
RULE_SIMPLE_EXPR,
RULE_SPAWN,
RULE_STATEMENT,
RULE_TIME_TAGGED_STATEMENT,
RULE_VM_MANAGEMENT,
TOKEN_HEX_CONST,
TOKEN_INT_CONST,
} from './vmlConstants';
import { decodeInt32Array } from './vmlTreeUtils';
import { decodeInt32Array, getVmlNameNode } from './vmlTreeUtils';

const sequenceEngineArgument: FswCommandArgumentInteger = {
arg_type: 'integer',
Expand All @@ -39,7 +45,10 @@ const sequenceEngineArgument: FswCommandArgumentInteger = {
units: '',
};

export function vmlTooltip(commandDictionary: CommandDictionary | null): Extension {
export function vmlTooltip(
commandDictionary: CommandDictionary | null,
librarySequenceMap: { [sequenceName: string]: LibrarySequence },
): Extension {
return hoverTooltip((view: EditorView, pos: number, side: number): Tooltip | null => {
const { from, to } = getTokenPositionInLine(view, pos);

Expand All @@ -52,83 +61,91 @@ export function vmlTooltip(commandDictionary: CommandDictionary | null): Extensi
const cursorNode = tree.cursorAt(from, 1).node;

const timeTaggedNode = getNearestAncestorNodeOfType(cursorNode, [RULE_TIME_TAGGED_STATEMENT]);

if (!timeTaggedNode) {
return null;
}
const statementSubNode = timeTaggedNode.getChild(RULE_STATEMENT)?.firstChild;

if (!statementSubNode) {
const statementNode = timeTaggedNode.getChild(RULE_STATEMENT);
if (!statementNode) {
return null;
}

switch (statementSubNode.name) {
case RULE_VM_MANAGEMENT:
{
if (
checkContainment(cursorNode, [
RULE_VM_MANAGEMENT,
undefined,
RULE_SIMPLE_EXPR,
RULE_CONSTANT,
TOKEN_INT_CONST,
])
) {
return argTooptip(sequenceEngineArgument, null, from, to);
}
const nameNode = getVmlNameNode(timeTaggedNode);
const commandName = nameNode ? unquoteUnescape(view.state.sliceDoc(nameNode.from, nameNode.to)) : null;
let command: FswCommand | undefined = undefined;

if (commandName) {
if (statementNode.getChild(RULE_ISSUE) || statementNode.getChild(RULE_ISSUE_DYNAMIC)) {
command = commandDictionary?.fswCommandMap[commandName];
} else if (statementNode.getChild(RULE_VM_MANAGEMENT)?.getChild(RULE_SPAWN)?.getChild(RULE_FUNCTION_NAME)) {
const librarySequence = librarySequenceMap[commandName];
if (librarySequence) {
command = librarySequenceToFswCommand(librarySequence);
}
break;
case RULE_ISSUE: {
const functionNameNode = statementSubNode.getChild(RULE_FUNCTION_NAME);
if (functionNameNode) {
const commandName = view.state.sliceDoc(functionNameNode.from, functionNameNode.to);
const command = commandDictionary?.fswCommandMap[commandName];
if (command) {
const callParametersNode = getNearestAncestorNodeOfType(cursorNode, [RULE_CALL_PARAMETERS]);
if (callParametersNode) {
const thisCallParameterNode =
cursorNode.name === RULE_CALL_PARAMETER
? cursorNode
: getNearestAncestorNodeOfType(cursorNode, [RULE_CALL_PARAMETER]);

if (thisCallParameterNode) {
const parameterNodes = callParametersNode.getChildren(RULE_CALL_PARAMETER);
const argIndex = parameterNodes.findIndex(
callParameterNode =>
callParameterNode.to === thisCallParameterNode.to &&
callParameterNode.from === thisCallParameterNode.from,
);

const arrayNode = thisCallParameterNode.getChild(RULE_BYTE_ARRAY);
if (arrayNode) {
const encodedValues = arrayNode
.getChildren(TOKEN_HEX_CONST)
.map(node => view.state.sliceDoc(node.from, node.to));
const decodedValue = decodeInt32Array(encodedValues);
if (decodedValue) {
return strTooltip(decodedValue, from, to);
}
}

if (argIndex > -1) {
const arg = command.arguments[argIndex];
if (arg) {
return argTooptip(arg, commandDictionary, from, to);
}
}
}
}

return cmdTooltip(command, from, to);
}
}
}

// cursor over command
if (command && nameNode?.from === from && nameNode?.to === to) {
return cmdTooltip(command, from, to);
}

// over seq engine
if (
checkContainment(cursorNode, [RULE_VM_MANAGEMENT, undefined, RULE_SIMPLE_EXPR, RULE_CONSTANT, TOKEN_INT_CONST])
) {
return argTooltip(sequenceEngineArgument, null, from, to);
}

const callParameterNode = getNearestAncestorNodeOfType(cursorNode, [RULE_CALL_PARAMETER]);
// over parameter
// handle variables
if (callParameterNode && command) {
const arrayNode = callParameterNode.getChild(RULE_BYTE_ARRAY);
if (arrayNode) {
const encodedValues = arrayNode
.getChildren(TOKEN_HEX_CONST)
.map(node => view.state.sliceDoc(node.from, node.to));
const decodedValue = decodeInt32Array(encodedValues);
if (decodedValue) {
return strTooltip(decodedValue, from, to);
}
}

return callParameterTooltip(callParameterNode, command, commandDictionary, from, to);
}

return null;
});
}

function callParameterTooltip(
callParameterNode: SyntaxNode,
command: FswCommand,
commandDictionary: CommandDictionary | null,
from: number,
to: number,
): Tooltip | null {
const callParametersNode = getNearestAncestorNodeOfType(callParameterNode, [RULE_CALL_PARAMETERS]);
if (!callParametersNode) {
return null;
}

const parameterNodes = callParametersNode.getChildren(RULE_CALL_PARAMETER);
const argIndex = parameterNodes.findIndex(
paramNode => callParameterNode.to === paramNode.to && callParameterNode.from === paramNode.from,
);

if (argIndex > -1) {
const arg = command.arguments[argIndex];
if (arg) {
return argTooltip(arg, commandDictionary, from, to);
}
}

return null;
}

function strTooltip(message: string, from: number, to: number) {
return {
above: true,
Expand All @@ -149,7 +166,7 @@ function strTooltip(message: string, from: number, to: number) {
};
}

function argTooptip(
function argTooltip(
arg: FswCommandArgument,
commandDictionary: CommandDictionary | null,
from: number,
Expand Down
62 changes: 33 additions & 29 deletions src/utilities/codemirror/vml/vmlTreeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,35 +108,7 @@ export class VmlCommandInfoMapper implements CommandInfoMapper {
}

getNameNode(timeTaggedStatementNode: SyntaxNode | null): SyntaxNode | null {
const ruleStatementNode = timeTaggedStatementNode?.getChild(RULE_STATEMENT);

const statementSubNode = ruleStatementNode?.getChild(GROUP_STATEMENT_SUB);
if (statementSubNode) {
switch (statementSubNode.name) {
case RULE_ISSUE:
return statementSubNode.getChild(RULE_FUNCTION_NAME);
case RULE_ISSUE_DYNAMIC:
// first call parameter is method
return (
statementSubNode
.getChild(RULE_CALL_PARAMETERS)
?.getChild(RULE_CALL_PARAMETER)
?.getChild(RULE_SIMPLE_EXPR)
?.getChild(RULE_CONSTANT)
?.getChild(TOKEN_STRING_CONST) ?? null
);
case RULE_VM_MANAGEMENT:
{
const spawnNode = statementSubNode.getChild(RULE_SPAWN);
if (spawnNode) {
return spawnNode.getChild(RULE_FUNCTION_NAME);
}
}
break;
}
}

return null;
return getVmlNameNode(timeTaggedStatementNode);
}

getVariables(docText: string, tree: Tree, position: number): string[] {
Expand Down Expand Up @@ -197,6 +169,38 @@ export class VmlCommandInfoMapper implements CommandInfoMapper {
}
}

export function getVmlNameNode(timeTaggedStatementNode: SyntaxNode | null): SyntaxNode | null {
const ruleStatementNode = timeTaggedStatementNode?.getChild(RULE_STATEMENT);

const statementSubNode = ruleStatementNode?.getChild(GROUP_STATEMENT_SUB);
if (statementSubNode) {
switch (statementSubNode.name) {
case RULE_ISSUE:
return statementSubNode.getChild(RULE_FUNCTION_NAME);
case RULE_ISSUE_DYNAMIC:
// first call parameter is method
return (
statementSubNode
.getChild(RULE_CALL_PARAMETERS)
?.getChild(RULE_CALL_PARAMETER)
?.getChild(RULE_SIMPLE_EXPR)
?.getChild(RULE_CONSTANT)
?.getChild(TOKEN_STRING_CONST) ?? null
);
case RULE_VM_MANAGEMENT:
{
const spawnNode = statementSubNode.getChild(RULE_SPAWN);
if (spawnNode) {
return spawnNode.getChild(RULE_FUNCTION_NAME);
}
}
break;
}
}

return null;
}

export function getArgumentPosition(argNode: SyntaxNode): number {
return (
getNearestAncestorNodeOfType(argNode, [RULE_STATEMENT])
Expand Down

0 comments on commit 9bcdafe

Please sign in to comment.