All files / src/cm6 hover-tooltip.ts

100% Statements 45/45
100% Branches 17/17
100% Functions 4/4
100% Lines 45/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 741x   1x                           1x 96x 96x           1x 105x 9x 9x   7x 7x 7x 7x   9x   5x 9x   4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 9x 1x 1x 9x 105x           1x 13x 13x 2x 13x 2x 13x 2x 13x 2x 13x 5x 13x 13x  
import { hoverTooltip } from '@codemirror/view';
import type { EditorView } from '@codemirror/view';
import {
  AstWalker,
  NodeType,
  SpelExpressionParser,
  VariableReference,
  PropertyOrFieldReference,
  BeanReference,
  TypeReference,
} from '@agentix-e/spel-ts';
import type { SpelNodeImpl } from '@agentix-e/spel-ts';
 
/**
 * Adapter: spel-ts AstWalker → CM6 hoverTooltip.
 */
export function spelHover() {
  return hoverTooltip(createHoverSource());
}
 
/**
 * Create the hover tooltip source function for CM6.
 * Exported for testing.
 */
export function createHoverSource() {
  return (view: EditorView, pos: number) => {
    const expression = view.state.sliceDoc();
    if (expression.trim().length === 0) return null;
 
    try {
      const parser = new SpelExpressionParser();
      const ast = parser.parseRaw(expression);
      const node = AstWalker.findNodeAt(ast, pos);
 
      if (!node) return null;
 
      const info = getNodeInfo(node);
      if (!info) return null;
 
      return {
        pos: node.startPos,
        end: node.endPos,
        create() {
          const dom = document.createElement('div');
          dom.style.cssText = 'padding: 4px 8px; font-size: 13px; max-width: 300px;';
          dom.textContent = info;
          return { dom };
        },
      };
    } catch {
      return null;
    }
  };
}
 
/**
 * Get hover tooltip text for a given AST node.
 * Exported for testing.
 */
export function getNodeInfo(node: SpelNodeImpl): string | null {
  switch (node.nodeType) {
    case NodeType.VARIABLE_REFERENCE:
      return `Variable: #${(node as VariableReference).getVariableName()}`;
    case NodeType.PROPERTY_OR_FIELD_REFERENCE:
      return `Property: .${(node as PropertyOrFieldReference).getName()}`;
    case NodeType.BEAN_REFERENCE:
      return `Spring Bean: @${(node as BeanReference).getBeanName()}`;
    case NodeType.TYPE_REFERENCE:
      return `Type: T(${(node as TypeReference).getTypeName()})`;
    default:
      return null;
  }
}