All files / src/lsp code_actions.ts

21.1% Statements 23/109
100% Branches 0/0
0% Functions 0/4
21.1% Lines 23/109

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 1091x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x                                                                                                     1x 1x 1x 1x                                                   1x 1x                   1x 1x
import * as LServer from "vscode-languageserver-types";
import {IRegistry} from "../_iregistry";
import {ICodeActionParams} from "./_interfaces";
import {Diagnostics} from "./diagnostics";
import {IEdit} from "../edit_helper";
import {Issue} from "../issue";
import {Position} from "../position";
import {LSPEdit} from "./_edit";
 
export class CodeActions {
  private readonly reg: IRegistry;
 
  public constructor(reg: IRegistry) {
    this.reg = reg;
  }
 
  public find(params: ICodeActionParams): LServer.CodeAction[] {
    const diag = new Diagnostics(this.reg);
    const issues = diag.findIssues(params.textDocument);
    const totals: {[key: string]: number} = {};
    const shown = new Set<string>();

    const ret: LServer.CodeAction[] = [];
    for (const i of issues) {
      const fix = i.getDefaultFix();
      if (fix !== undefined) {
        if (totals[i.getKey()] === undefined) {
          totals[i.getKey()] = 1;
        } else {
          totals[i.getKey()]++;
        }

        if (this.inRange(i, params.range) === true) {
          ret.push({
            title: "Apply fix, " + i.getKey(),
            kind: LServer.CodeActionKind.QuickFix,
            diagnostics: [Diagnostics.mapDiagnostic(i)],
            isPreferred: true,
            edit: LSPEdit.mapEdit(fix),
          });
          shown.add(i.getKey());
        }
      }

      for (const alternative of i.getAlternativeFixes() || []) {
        if (this.inRange(i, params.range) === true) {
          ret.push({
            title: alternative.description,
            kind: LServer.CodeActionKind.QuickFix,
            diagnostics: [Diagnostics.mapDiagnostic(i)],
            isPreferred: true,
            edit: LSPEdit.mapEdit(alternative.edit),
          });
          shown.add(i.getKey());
        }
      }
    }

    for (const s of shown) {
      if (totals[s] > 1) {
        const foo = this.fixAlls(s, issues);
        ret.push(foo);
      }
    }

    return ret;
  }
 
//////////////////////
 
  private fixAlls(key: string, issues: readonly Issue[]): LServer.CodeAction {
    const diagnostics: LServer.Diagnostic[] = [];
    const fixes: IEdit[] = [];

    for (const i of issues) {
      if (i.getKey() !== key) {
        continue;
      }

      const fix = i.getDefaultFix();
      if (fix === undefined) {
        continue;
      }

      fixes.push(fix);
      diagnostics.push(Diagnostics.mapDiagnostic(i));
    }

    return {
      title: "Fix all, " + key,
      kind: LServer.CodeActionKind.QuickFix,
      diagnostics,
      isPreferred: true,
      edit: LSPEdit.mapEdits(fixes),
    };
  }
 
  private inRange(i: Issue, range: LServer.Range): boolean {
    const start = new Position(range.start.line + 1, range.start.character + 1);
    const end = new Position(range.end.line + 1, range.end.character + 1);

    return i.getStart().isBetween(start, end)
      || i.getEnd().isBetween(start, end)
      || start.isBetween(i.getStart(), i.getEnd())
      || end.isBetween(i.getStart(), i.getEnd())
      || end.equals(i.getEnd());
  }
 
}