All files / client/src/hooks use-sketch-analysis.ts

97.05% Statements 33/34
87.5% Branches 21/24
100% Functions 10/10
96.29% Lines 26/27

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 74 75 76 77 78 79 80                                    28x 23x         13x   8x 8x           32x       13x 3x 1x             18x 13x 13x 13x 13x   13x 23x 32x 20x 20x   32x 6x 6x         13x 13x   13x         18x            
import { useMemo } from "react";
import {
  analyzeStaticIO,
  analyzeStaticIOProject,
  type StaticIOCall,
} from "@shared/io-registry-parser";
import type { PinMode } from "@shared/types/arduino.types";
import type { SourceProject } from "@shared/source-project";
 
interface SketchAnalysisResult {
  analogPins: number[]; // concrete Arduino pin numbers (A0 -> 14)
  varMap: Record<string, number>;
  detectedPinModes: Record<number, PinMode>;
  pendingPinConflicts: number[]; // pins that are both used as analogRead and declared via pinMode
  digitalPinsFromPinMode: number[];
}
 
function toAnalogBoardPin(pinId: number): number | undefined {
  if (pinId >= 0 && pinId <= 5) return pinId + 14;
  Eif (pinId >= 14 && pinId <= 19) return pinId;
  return undefined;
}
 
function projectVarMap(symbols: Record<string, number>): Record<string, number> {
  return Object.fromEntries(
    Object.entries(symbols).flatMap(([name, pinId]) => {
      const analogPin = toAnalogBoardPin(pinId);
      return analogPin === undefined ? [] : [[name, analogPin]];
    }),
  );
}
 
function isVisibleAnalogRead(call: StaticIOCall): boolean {
  return call.op === "analogRead" && call.loopBody !== "braceless";
}
 
function analyzeSource(source: string | SourceProject | null | undefined) {
  if (typeof source === "string") return analyzeStaticIO(source);
  if (source) return analyzeStaticIOProject(source);
  return { pins: [], unresolvedCalls: [], symbols: {} };
}
 
// Hook: pure projection of the canonical static I/O analysis for board state.
export function useSketchAnalysis(
  source: string | SourceProject | null | undefined,
): SketchAnalysisResult {
  return useMemo(() => {
    const analysis = analyzeSource(source);
    const analogPins = new Set<number>();
    const pinModePins = new Set<number>();
    const detectedPinModes: Record<number, PinMode> = {};
 
    for (const { pinId, calls } of analysis.pins) {
      for (const call of calls) {
        if (isVisibleAnalogRead(call)) {
          const analogPin = toAnalogBoardPin(pinId);
          Eif (analogPin !== undefined) analogPins.add(analogPin);
        }
        if (call.op === "pinMode" && call.mode !== undefined) {
          pinModePins.add(pinId);
          detectedPinModes[pinId] = call.mode;
        }
      }
    }
 
    const sortedAnalogPins = [...analogPins].sort((a, b) => a - b);
    const sortedPinModePins = [...pinModePins].sort((a, b) => a - b);
 
    return {
      analogPins: sortedAnalogPins,
      varMap: projectVarMap(analysis.symbols),
      detectedPinModes,
      pendingPinConflicts: sortedAnalogPins.filter((pin) =>
        pinModePins.has(pin)
      ),
      digitalPinsFromPinMode: sortedPinModePins,
    };
  }, [source]);
}