summaryrefslogtreecommitdiff
path: root/extension/react-app/src/util/editCache.ts
blob: b8071127d3ea23b4bcdb34d6e6327941a5df8784 (plain)
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
import { useApi } from "../util/api";
import { FileEdit, SerializedDebugContext } from "../../../src/client";
import { useCallback, useEffect, useState } from "react";

export function useEditCache() {
  const { debugApi } = useApi();

  const fetchNewEdit = useCallback(
    async (debugContext: SerializedDebugContext) => {
      return (
        await debugApi?.editEndpointDebugEditPost({
          serializedDebugContext: debugContext,
        })
      )?.completion;
    },
    [debugApi]
  );

  const [editCache, setEditCache] = useState(new EditCache(fetchNewEdit));

  useEffect(() => {
    setEditCache(new EditCache(fetchNewEdit));
  }, [fetchNewEdit]);

  return editCache;
}

/**
 * Stores preloaded edits, invalidating based off of debug context changes
 */
class EditCache {
  private _lastDebugContext: SerializedDebugContext | undefined;
  private _cachedEdits: FileEdit[] | undefined;
  private _fetchNewEdit: (
    debugContext: SerializedDebugContext
  ) => Promise<FileEdit[] | undefined>;
  private _debounceTimer: NodeJS.Timeout | undefined;

  private _debugContextChanged(debugContext: SerializedDebugContext): boolean {
    if (!this._lastDebugContext) {
      return true;
    }

    return (
      JSON.stringify(this._lastDebugContext) !== JSON.stringify(debugContext)
    );
  }

  private _debugContextComplete(debugContext: SerializedDebugContext): boolean {
    return debugContext.rangesInFiles.length > 0;
  }

  public async preloadEdit(debugContext: SerializedDebugContext) {
    if (this._debounceTimer) {
      clearTimeout(this._debounceTimer);
    }
    if (
      this._debugContextComplete(debugContext) &&
      this._debugContextChanged(debugContext)
    ) {
      this._debounceTimer = setTimeout(async () => {
        console.log("Preloading edits");
        this._cachedEdits = await this._fetchNewEdit(debugContext);
        this._lastDebugContext = debugContext;
      }, 200);
    }
  }

  public async getEdit(
    debugContext: SerializedDebugContext
  ): Promise<FileEdit[]> {
    if (this._debugContextChanged(debugContext)) {
      console.log("Cache miss");
      this._cachedEdits = await this._fetchNewEdit(debugContext);
    } else {
      console.log("Cache hit");
    }

    return this._cachedEdits!;
  }

  constructor(
    fetchNewEdit: (
      debugContext: SerializedDebugContext
    ) => Promise<FileEdit[] | undefined>
  ) {
    this._fetchNewEdit = fetchNewEdit;
  }
}