diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/explore/consts.ts b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/explore/consts.ts index acbc6a9f10..8d99ff3be3 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/explore/consts.ts +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/explore/consts.ts @@ -41,3 +41,56 @@ export const monacoOptions: editor.IStandaloneEditorConstructionOptions = { automaticLayout: true, fixedOverflowWidgets: true, }; + +export const monacoSuggestionsMap = { + KEYWORD: "Keyword", + TABLE: "Field", + COLUMN: "Field", +} as const; + +export const suggestedSQLKeywords = [ + // Basic queries + "SELECT", + "FROM", + "WHERE", + "GROUP BY", + "ORDER BY", + "LIMIT", + "OFFSET", + // Joins + "JOIN", + "INNER JOIN", + "LEFT JOIN", + "RIGHT JOIN", + "FULL JOIN", + "ON", + // Aggregates + "COUNT", + "SUM", + "AVG", + "MAX", + "MIN", + // Conditions + "AND", + "OR", + "NOT", + "IN", + "BETWEEN", + "LIKE", + "IS NULL", + "IS NOT NULL", + // Other clauses + "HAVING", + "DISTINCT", + "AS", + "WITH", + // Sorting + "ASC", + "DESC", + // Set operations + "UNION", + "UNION ALL", + "INTERSECT", + "EXCEPT", +] as const; +export type SuggestedSQLKeyword = (typeof suggestedSQLKeywords)[number]; diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/explore/useMonacoSuggestions.ts b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/explore/useMonacoSuggestions.ts index 89dd2a211e..f1eafe5bed 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/explore/useMonacoSuggestions.ts +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/explore/useMonacoSuggestions.ts @@ -1,13 +1,12 @@ import { useEffect } from "react"; import { Table } from "@latticexyz/config"; import { useMonaco } from "@monaco-editor/react"; +import { SuggestedSQLKeyword, monacoSuggestionsMap, suggestedSQLKeywords } from "./consts"; import { useQueryAutocomplete } from "./useQueryAutocomplete"; -const monacoSuggestionsMap = { - KEYWORD: "Keyword", - TABLE: "Field", - COLUMN: "Field", -} as const; +const isSuggestedSQLKeyword = (keyword: string): keyword is SuggestedSQLKeyword => { + return (suggestedSQLKeywords as readonly string[]).includes(keyword); +}; export function useMonacoSuggestions(table?: Table) { const monaco = useMonaco(); @@ -39,17 +38,18 @@ export function useMonacoSuggestions(table?: Table) { endColumn: word.endColumn, }; - const suggestions = queryAutocomplete - .autocomplete(textUntilPosition) + const suggestions = Array.from( + // autocomplete can return duplicates, so we need to deduplicate + new Map(queryAutocomplete.autocomplete(textUntilPosition).map((item) => [item.value, item])).values(), + ) + .filter(({ value, optionType }) => (optionType === "KEYWORD" ? isSuggestedSQLKeyword(value) : !!value)) .map(({ value, optionType }) => ({ label: value, kind: monaco.languages.CompletionItemKind[monacoSuggestionsMap[optionType]], - insertText: `"${value}"`, + insertText: optionType === "KEYWORD" ? value : `"${value}"`, range, - // move keyword optionType to the top of suggestions list sortText: optionType !== "KEYWORD" ? "0" : "1", - })) - .filter(({ label }) => !!label); + })); return { suggestions }; },