All files / src/lsp implementation.ts

90.58% Statements 77/85
70.58% Branches 12/17
100% Functions 4/4
90.58% Lines 77/85

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 851x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 1x 1x 4x 4x 4x 4x     4x 4x     4x 4x 4x     4x 4x 4x 2x 2x 2x 4x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x 1x 1x 8x 8x 8x 1x 1x 1x 1x 1x 1x 1x 8x 8x 7x 7x 8x 8x 8x 1x 1x
import * as LServer from "vscode-languageserver-types";
import {IRegistry} from "../_iregistry";
import {ABAPObject} from "../objects/_abap_object";
import {LSPUtils} from "./_lsp_utils";
import {LSPLookup} from "./_lookup";
import {MethodDefinition} from "../abap/types";
import {SyntaxLogic} from "../abap/5_syntax/syntax";
import {ISpaghettiScopeNode} from "../abap/5_syntax/_spaghetti_scope";
import {ReferenceType} from "../abap/5_syntax/_reference";
import {Identifier} from "../abap/4_file_information/_identifier";
 
// note: finding implementations might be slow, ie finding method implementations currently searches the full registry
 
// go to implementation
export class Implementation {
  private readonly reg: IRegistry;
 
  public constructor(reg: IRegistry) {
    this.reg = reg;
  }
 
  public find(textDocument: LServer.TextDocumentIdentifier,
              position: LServer.Position): LServer.Location[] {
 
    const file = LSPUtils.getABAPFile(this.reg, textDocument.uri);
    if (file === undefined) {
      return [];
    }
    const obj = this.reg.getObject(file.getObjectType(), file.getObjectName());
    if (!(obj instanceof ABAPObject)) {
      return [];
    }
 
    const found = LSPUtils.findCursor(this.reg, {textDocument, position});
    if (found === undefined) {
      return [];
    }
 
    const lookup = LSPLookup.lookup(found, this.reg, obj);
    if (lookup?.implementation) {
      return [lookup?.implementation];
    }
 
    if (lookup?.definitionId instanceof MethodDefinition) {
      return this.findMethodImplementations(lookup.definitionId);
    }
 
    return [];
  }
 
  private findMethodImplementations(def: MethodDefinition): LServer.Location[] {
    const ret: LServer.Location[] = [];
 
    // note that this searches _everything_
    for (const obj of this.reg.getObjects()) {
      if (this.reg.isDependency(obj) || !(obj instanceof ABAPObject)) {
        continue;
      }
      const found = this.searchReferences(new SyntaxLogic(this.reg, obj).run().spaghetti.getTop(), def);
      ret.push(...found);
    }
 
    return ret;
  }
 
  private searchReferences(scope: ISpaghettiScopeNode, id: Identifier): LServer.Location[] {
    const ret: LServer.Location[] = [];
 
    for (const r of scope.getData().references) {
      if (r.referenceType === ReferenceType.MethodImplementationReference
          && r.resolved
          && r.resolved.getFilename() === id.getFilename()
          && r.resolved.getStart().equals(id.getStart())) {
        ret.push(LSPUtils.identiferToLocation(r.position));
      }
    }
 
    for (const c of scope.getChildren()) {
      ret.push(...this.searchReferences(c, id));
    }
 
    return ret;
  }
 
}