diff --git a/components/LSP/useLanguageServer.js b/components/LSP/useLanguageServer.js
index ff3e2d2..494f7e7 100644
--- a/components/LSP/useLanguageServer.js
+++ b/components/LSP/useLanguageServer.js
@@ -1,9 +1,13 @@
-import { useState, useEffect } from "react";
+import { useState, useEffect, useMemo, useRef } from "react";
import { monaco } from "react-monaco-editor";
import { MonacoServices } from "monaco-languageclient/lib/monaco-services";
import { CadenceLanguageServer } from "./language-server";
import { createCadenceLanguageClient } from "./language-client";
+import { useNetworkContext } from "../../contexts/NetworkContext";
+import { setEnvironment } from "flow-cadut";
+import { useRegistryContext } from "../Registry";
+import { debounce } from "../../utils";
let monacoServicesInstalled = false;
@@ -39,6 +43,9 @@ const launchLanguageClient = async (
};
export default function useLanguageServer() {
+ const network = useNetworkContext() || "testnet";
+ const { registry, contracts, getContractCode } = useRegistryContext();
+
let initialCallbacks = {
// The actual callback will be set as soon as the language server is initialized
toServer: null,
@@ -63,28 +70,44 @@ export default function useLanguageServer() {
const [languageClient, setLanguageClient] = useState(null);
const [callbacks, setCallbacks] = useState(initialCallbacks);
- const getCode = (address) => {
- // TODO: Fetch code from address.
- /*
- This is probably can't be implemented right now as server expects only address,
- but not the name of the contract.
- */
- return "";
+ const timer = useRef(null)
+
+ const getCode = (importStatement) => {
+ const [address,contractName] = importStatement.split(".")
+ const code = getContractCode(contractName, address) || ""
+ if(code === ""){
+ console.log(`%c+++++++++++++ NOT FOUND!!!!!!!!!! ${contractName}`,"color: red")
+ }
+ return code
};
- const restartServer = () => {
+ const restartServer = ()=>{
console.log("Restarting server...");
startLanguageServer(callbacks, getCode, {
setLanguageServer,
setCallbacks,
}).then();
- };
+ }
+
+ const debouncedRestart = () => {
+ if(timer.current){
+ clearTimeout(timer.current);
+ }
+ timer.current = setTimeout(()=>{
+ console.log("Restart language server")
+ if(languageServer){
+ languageServer.updateCodeGetter(getCode)
+ // restartServer()
+ }
+ }, 2000);
+ }
useEffect(() => {
// The Monaco Language Client services have to be installed globally, once.
// An editor must be passed, which is only used for commands.
// As the Cadence language server is not providing any commands this is OK
+ setEnvironment(network);
console.log("Installing monaco services");
if (!monacoServicesInstalled) {
@@ -101,6 +124,12 @@ export default function useLanguageServer() {
}
}, [languageServer]);
+ useEffect(()=>{
+ if(languageServer){
+ languageServer.updateCodeGetter(getCode)
+ }
+ },[registry, contracts])
+
return {
languageClient,
languageServer,
diff --git a/components/Registry/hooks.js b/components/Registry/hooks.js
new file mode 100644
index 0000000..29d1028
--- /dev/null
+++ b/components/Registry/hooks.js
@@ -0,0 +1,88 @@
+import { useEffect, useReducer, useState } from "react";
+import { extendEnvironment, extractImports } from "flow-cadut";
+import * as fcl from "@onflow/fcl";
+
+import { fetchRegistry, prepareEnvironments } from "../../utils";
+import { useNetworkContext } from "../../contexts/NetworkContext";
+
+const contractReducer = (state, action) => {
+ const { contracts, network } = action;
+
+ return {
+ ...state,
+ [network]: {
+ ...state[network],
+ ...contracts,
+ },
+ };
+};
+
+export const useRegistry = () => {
+ const network = useNetworkContext() || "testnet";
+ const [registry, setRegistry] = useState({});
+ const [contracts, dispatch] = useReducer(contractReducer, {
+ testnet: {},
+ mainnet: {},
+ });
+
+ const getRegistry = async () => {
+ const data = await fetchRegistry();
+ const registry = prepareEnvironments(data);
+ extendEnvironment(registry);
+ setRegistry(registry);
+ };
+
+ useEffect(() => {
+ getRegistry().then();
+ }, []);
+
+ const fetchDependencies = (list) => {
+ const keys = Object.keys(list);
+ for (let i = 0; i < keys.length; i++) {
+ // TODO: check if it's cached
+ const name = keys[i];
+ const address = list[name];
+ const contract = contracts[network][name];
+ if (!contract) {
+ fetchContract(name, address);
+ }
+ }
+ };
+
+ const fetchContract = async (name, exactAddress) => {
+ let address = exactAddress;
+ if (!exactAddress && registry[network]) {
+ address = registry[network][name]
+ }
+ if(!address){
+ return false
+ }
+ try {
+ console.log("--------> ADDRESS:", {name, exactAddress, address})
+ const { contracts } = await fcl
+ .send([fcl.getAccount(address)])
+ .then(fcl.decode);
+ const code = contracts[name] || "";
+ const dependencies = extractImports(code);
+ fetchDependencies(dependencies);
+
+ // Update state
+ dispatch({
+ contracts,
+ network,
+ });
+ } catch (e) {
+ console.error(e);
+ }
+ };
+
+ const getContractCode = (name, address) => {
+ const contract = contracts[network][name];
+ if(!contract){
+ fetchContract(name, address).then();
+ }
+ return contract || "";
+ };
+
+ return { registry, contracts, fetchContract, getContractCode };
+};
diff --git a/components/Registry/index.js b/components/Registry/index.js
new file mode 100644
index 0000000..3c8312c
--- /dev/null
+++ b/components/Registry/index.js
@@ -0,0 +1,15 @@
+import { createContext, useContext } from "react";
+import { useRegistry } from "./hooks";
+
+export const RegistryContext = createContext({});
+export const useRegistryContext = () => useContext(RegistryContext);
+
+export default function RegistryProvider(props) {
+ const registry = useRegistry();
+ const { children } = props;
+ return (
+