All files / src/lsp code_lens.ts

87.5% Statements 98/112
47.61% Branches 10/21
100% Functions 4/4
87.5% Lines 98/112

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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 1121x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 1x 1x 3x 3x 3x     3x 3x 3x     3x 3x 3x 3x 3x 3x 3x 1x 1x     1x 1x     1x 1x 1x 1x 3x 3x 3x 1x     1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 12x 12x 12x 6x 3x 1x 1x 3x 6x 12x 12x 9x 9x 12x 12x 12x 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 {MessageClass} from "../objects";
import {ISpaghettiScopeNode} from "../abap/5_syntax/_spaghetti_scope";
import {IReference, ReferenceType} from "../abap/5_syntax/_reference";
import {MethodDefinition} from "../abap/types";
import {ABAPFile} from "../abap/abap_file";
 
export type CodeLensSettings = {
  messageText: boolean,
  dynamicExceptions: boolean,
};
 
export class CodeLens {
  private readonly reg: IRegistry;
 
  public constructor(reg: IRegistry) {
    this.reg = reg;
  }
 
  public list(textDocument: LServer.TextDocumentIdentifier,
              settings: CodeLensSettings = {messageText: true, dynamicExceptions: true}): LServer.CodeLens[] {
    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.CodeLens[] = [];
 
    if (settings.messageText === true) {
      const list = this.reg.getMSAGReferences().listByFilename(file.getFilename());
      for (const l of list) {
        const msag = this.reg.getObject("MSAG", l.messageClass) as MessageClass | undefined;
        if (msag === undefined) {
          continue;
        }
        const text = msag.getByNumber(l.number)?.getMessage();
        if (text === undefined) {
          continue;
        }
        ret.push({
          range: LSPUtils.tokenToRange(l.token),
          command: LServer.Command.create(text, "")});
      }
    }
    if (settings.dynamicExceptions === true) {
      for (const ref of this.findMethodReferences(top, file)) {
        if (!(ref.resolved instanceof MethodDefinition)) {
          continue;
        }
        let text = "";
        for (const e of ref.resolved.getRaising()) {
          if (this.isDynamicException(e, top)) {
            if (text === "") {
              text = "Dynamic Exceptions: ";
            } else {
              text += " & ";
            }
            text += e.toUpperCase();
          }
        }
        if (text !== "") {
          ret.push({
            range: LSPUtils.tokenToRange(ref.position.getToken()),
            command: LServer.Command.create(text, "")});
        }
      }
    }
 
    return ret;
  }
 
  private isDynamicException(name: string, top: ISpaghettiScopeNode) {
    // todo: this method only works with global exceptions?
    let current: string | undefined = name;
    while (current !== undefined) {
      if (current.toUpperCase() === "CX_DYNAMIC_CHECK") {
        return true;
      }
      current = top.findClassDefinition(current)?.getSuperClass();
    }
    return false;
  }
 
  private findMethodReferences(node: ISpaghettiScopeNode, file: ABAPFile): IReference[] {
    const ret: IReference[] = [];
 
    if (node.getIdentifier().filename === file.getFilename()) {
      for (const r of node.getData().references) {
        if (r.referenceType === ReferenceType.MethodReference) {
          ret.push(r);
        }
      }
    }
 
    for (const c of node.getChildren()) {
      ret.push(...this.findMethodReferences(c, file));
    }
 
    return ret;
  }
 
}