All files / src/lsp implementation.ts

90.59% Statements 77/85
72.22% Branches 13/18
100% Functions 4/4
90.59% 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 3x 3x 1x 1x 3x 3x 3x 3x     3x 3x     3x 3x 3x     3x 3x 3x 1x 1x 2x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 1x 1x 1x 1x 1x 1x 1x 6x 6x 5x 5x 6x 6x 6x 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;
  }
 
}