All files / src/lsp inlay_hints.ts

92.85% Statements 78/84
76.92% Branches 10/13
100% Functions 3/3
92.85% Lines 78/84

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 81 82 83 841x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 9x 9x 1x 1x 9x 9x     9x 9x 9x     9x 9x 9x 9x 9x 9x 9x 17x 17x 17x 16x 17x 1x 1x 17x 17x     17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 9x 9x 9x 9x 1x 1x 32x 32x 32x 31x 31x 23x 31x 17x 17x 31x 32x 32x 23x 23x 32x 32x 32x 1x 1x
import * as LServer from "vscode-languageserver-types";
import {IRegistry} from "../_iregistry";
import {LSPUtils} from "./_lsp_utils";
import {SyntaxLogic} from "../abap/5_syntax/syntax";
import {ABAPObject} from "../objects/_abap_object";
import {ISpaghettiScopeNode} from "../abap/5_syntax/_spaghetti_scope";
import {ReferenceType, IReference} from "../abap/5_syntax/_reference";
import {TypedIdentifier} from "../abap/types/_typed_identifier";
import {ClassDefinition} from "../abap/types";
 
export type InlayHintsSettings = {
  inferredTypes: boolean,
};
 
export class InlayHints {
  private readonly reg: IRegistry;
 
  public constructor(reg: IRegistry) {
    this.reg = reg;
  }
 
  public list(textDocument: LServer.TextDocumentIdentifier, settings: InlayHintsSettings = {inferredTypes: true}): LServer.InlayHint[] {
    const file = LSPUtils.getABAPFile(this.reg, textDocument.uri);
    if (file === undefined) {
      return [];
    }
 
    const obj = this.reg.findObjectForFile(file);
    if (obj === undefined || !(obj instanceof ABAPObject)) {
      return [];
    }
    const top = new SyntaxLogic(this.reg, obj).run().spaghetti.getTop();
 
    const ret: LServer.InlayHint[] = [];
 
    if (settings.inferredTypes === true) {
      const implicit = this.findImplicitReferences(top);
      for (const i of implicit) {
 
        let label: string | undefined = undefined;
        if (i.resolved instanceof TypedIdentifier) {
          label = "TYPE " + i.resolved.getType().toABAP();
        } else if (i.resolved instanceof ClassDefinition) {
          label = "TYPE REF TO " + i.resolved.getName();
        }
 
        if (label === undefined) {
          continue;
        }
 
        ret.push({
          label: label,
          tooltip: "Inferred type",
          kind: LServer.InlayHintKind.Type,
          paddingLeft: true,
          paddingRight: true,
          position: LSPUtils.positionToLS(i.position.getEnd()),
        });
      }
    }
 
    return ret;
  }
 
  private findImplicitReferences(node: ISpaghettiScopeNode): IReference[] {
    const ret: IReference[] = [];
 
    for (const r of node.getData().references) {
      if (
        r.referenceType === ReferenceType.InferredType ||
        r.referenceType === ReferenceType.DataWriteReference
      ) {
        ret.push(r);
      }
    }
 
    for (const c of node.getChildren()) {
      ret.push(...this.findImplicitReferences(c));
    }
 
    return ret;
  }
 
}