Skip to content

Commit

Permalink
Execute queries from the variables panel (#509)
Browse files Browse the repository at this point in the history
  • Loading branch information
macjuul authored Oct 9, 2024
1 parent b0c0d9c commit a683897
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 189 deletions.
36 changes: 0 additions & 36 deletions src/editor/commands.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,5 @@
import { startCompletion } from "@codemirror/autocomplete";
import type { Command, EditorView } from "@codemirror/view";
import {
executeGraphql,
executeUserQuery,
} from "~/screens/database/connection/connection";
import { getActiveConnection } from "~/util/connection";
import { tryParseParams } from "~/util/helpers";

/**
* Execute the contents of the editor as a query
*/
export const executeEditorQuery: Command = (view: EditorView) => {
console.log(1);
const query = view.state.doc.toString();
const selection = view.state.selection.main;

executeUserQuery({
override:
selection?.empty === false
? query.slice(selection.from, selection.to)
: query,
});

return true;
};

/**
* Execute the contents of the editor as a GraphQL query
*/
export const executeGraphqlEditorQuery: Command = () => {
const connection = getActiveConnection();
const params = tryParseParams(connection.graphqlVariables);

executeGraphql(connection.graphqlQuery, params);

return true;
};

/**
* Suggest completions at the start of each line
Expand Down
24 changes: 3 additions & 21 deletions src/editor/graphql.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,12 @@
import type { CompletionSource } from "@codemirror/autocomplete";
import { syntaxTree } from "@codemirror/language";
import {
type Extension,
Prec,
StateEffect,
StateField,
} from "@codemirror/state";
import { type Extension, Prec, StateEffect, StateField } from "@codemirror/state";
import { EditorView, ViewPlugin, keymap } from "@codemirror/view";
import {
type Position,
fillAllFieldsCommands,
graphqlLanguage,
} from "cm6-graphql";
import {
type DocumentNode,
type GraphQLList,
type GraphQLNonNull,
type GraphQLObjectType,
type GraphQLSchema,
parse,
} from "graphql";
import { type Position, fillAllFieldsCommands, graphqlLanguage } from "cm6-graphql";
import { type DocumentNode, type GraphQLSchema, parse } from "graphql";
import { fillGraphqlFields } from "./keybinds";
import { graphqlSuggestions } from "./keymaps";

type NodeType = GraphQLNonNull<GraphQLList<GraphQLNonNull<GraphQLObjectType>>>;

const documentEffect = StateEffect.define<DocumentNode | undefined>();

const documentField = StateField.define<DocumentNode | undefined>({
Expand Down
7 changes: 2 additions & 5 deletions src/editor/keymaps.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { redo, redoSelection, undo, undoSelection } from "@codemirror/commands";
import type { KeyBinding } from "@codemirror/view";
import {
executeEditorQuery,
executeGraphqlEditorQuery,
suggestCompletions,
} from "./commands";
import { suggestCompletions } from "./commands";
import { executeEditorQuery, executeGraphqlEditorQuery } from "./query";

/**
* A custom variant of the history keymap that uses
Expand Down
65 changes: 65 additions & 0 deletions src/editor/query.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { StateEffect, StateField } from "@codemirror/state";
import type { Command, EditorView } from "@codemirror/view";
import { executeGraphql, executeUserQuery } from "~/screens/database/connection/connection";
import { getActiveConnection } from "~/util/connection";
import { tryParseParams } from "~/util/helpers";

const queryEditorEffect = StateEffect.define<EditorView>();

/**
* The field holding the target query editor
*/
export const queryEditorField = StateField.define<EditorView | undefined>({
create() {
return undefined;
},
update(set, tr) {
for (const e of tr.effects) {
if (e.is(queryEditorEffect)) {
return e.value;
}
}

return set;
},
});

/**
* Set the query editor the current view is using
*
* @param currentView The view to update
* @param queryView The view holding the query
*/
export function setQueryEditor(currentView: EditorView, queryView?: EditorView) {
currentView.dispatch({
effects: queryEditorEffect.of(queryView ?? currentView),
});
}

/**
* Execute the contents of the editor as a query
* The source editor can be overriden using the `queryEditorEffect`
*/
export const executeEditorQuery: Command = (view: EditorView) => {
const editor = view.state.field(queryEditorField, false) ?? view;
const query = editor.state.doc.toString();
const selection = editor.state.selection.main;

executeUserQuery({
override: selection?.empty === false ? query.slice(selection.from, selection.to) : query,
});

return true;
};

/**
* Execute the contents of the editor as a GraphQL query
*/
export const executeGraphqlEditorQuery: Command = () => {
const connection = getActiveConnection();
const params = tryParseParams(connection.graphqlVariables);

executeGraphql(connection.graphqlQuery, params);

return true;
};
48 changes: 30 additions & 18 deletions src/screens/database/views/graphql/GraphqlView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Icon } from "~/components/Icon";
import { Introduction } from "~/components/Introduction";
import { PanelDragger } from "~/components/Pane/dragger";
import { GQL_SUPPORTED } from "~/constants";
import { executeGraphqlEditorQuery } from "~/editor/commands";
import { executeGraphqlEditorQuery } from "~/editor/query";
import { useActiveConnection, useIsConnected } from "~/hooks/connection";
import { useGraphqlIntrospection } from "~/hooks/graphql";
import { useStable } from "~/hooks/stable";
Expand Down Expand Up @@ -85,12 +85,13 @@ export function GraphqlView() {
);

useIntent("run-graphql-query", () => {});
useIntent("toggle-graphql-variables", () =>
setShowVariables(!showVariables),
);
useIntent("toggle-graphql-variables", () => setShowVariables(!showVariables));

return isAvailable ? (
<Stack gap="md" h="100%">
<Stack
gap="md"
h="100%"
>
<PanelGroup direction="horizontal">
<Panel minSize={15}>
<PanelGroup direction="vertical">
Expand All @@ -110,7 +111,10 @@ export function GraphqlView() {
{showVariables && (
<>
<PanelDragger />
<Panel defaultSize={40} minSize={35}>
<Panel
defaultSize={40}
minSize={35}
>
<VariablesPaneLazy
isValid={variablesValid}
setIsValid={setVariablesValid}
Expand All @@ -122,7 +126,10 @@ export function GraphqlView() {
</PanelGroup>
</Panel>
<PanelDragger>
<Center pos="relative" h="100%">
<Center
pos="relative"
h="100%"
>
<Paper
className={classes.sendCircle}
bg={isLight ? "slate.0" : "slate.9"}
Expand All @@ -142,7 +149,10 @@ export function GraphqlView() {
isValid && classes.sendButtonValid,
)}
>
<Icon path={iconCursor} size="lg" />
<Icon
path={iconCursor}
size="lg"
/>
</ActionIcon>
</Paper>
</Center>
Expand All @@ -153,27 +163,29 @@ export function GraphqlView() {
</PanelGroup>
</Stack>
) : (
<Introduction title="GraphQL" icon={iconGraphql}>
<Introduction
title="GraphQL"
icon={iconGraphql}
>
<Text>
The GraphQL view provides a fully interactive environment for
executing GraphQL queries against your database.
The GraphQL view provides a fully interactive environment for executing GraphQL
queries against your database.
</Text>
<Group gap="sm" c="pink">
<Group
gap="sm"
c="pink"
>
<Icon path={iconWarning} />
<Text>
GraphQL is not supported{" "}
{isSandbox
? "in the sandbox"
: "by your current connection"}
{isSandbox ? "in the sandbox" : "by your current connection"}
</Text>
</Group>
<Button
color="slate"
variant="light"
rightSection={<Icon path={iconOpen} />}
onClick={() =>
adapter.openUrl("https://surrealdb.com/docs/surrealist")
}
onClick={() => adapter.openUrl("https://surrealdb.com/docs/surrealist")}
>
Learn more
</Button>
Expand Down
60 changes: 21 additions & 39 deletions src/screens/database/views/query/QueryPane/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
import { Prec, type SelectionRange } from "@codemirror/state";
import { type EditorView, keymap, lineNumbers } from "@codemirror/view";
import { ActionIcon, Group, Stack, Tooltip } from "@mantine/core";
import { Text } from "@mantine/core";
import { surrealql } from "@surrealdb/codemirror";
import { type HtmlPortalNode, OutPortal } from "react-reverse-portal";
import { CodeEditor } from "~/components/CodeEditor";
import { Icon } from "~/components/Icon";
import { ContentPane } from "~/components/Pane";
import {
runQueryKeymap,
selectionChanged,
Expand All @@ -16,20 +7,24 @@ import {
surqlTableCompletion,
surqlVariableCompletion,
} from "~/editor";

import { Prec, type SelectionRange } from "@codemirror/state";
import { type EditorView, keymap, lineNumbers } from "@codemirror/view";
import { ActionIcon, Group, Stack, Tooltip } from "@mantine/core";
import { Text } from "@mantine/core";
import { surrealql } from "@surrealdb/codemirror";
import { type HtmlPortalNode, OutPortal } from "react-reverse-portal";
import { CodeEditor } from "~/components/CodeEditor";
import { Icon } from "~/components/Icon";
import { ContentPane } from "~/components/Pane";
import { useDebouncedFunction } from "~/hooks/debounce";
import { useStable } from "~/hooks/stable";
import { useIntent } from "~/hooks/url";
import { useInspector } from "~/providers/Inspector";
import { useConfigStore } from "~/stores/config";
import type { TabQuery } from "~/types";
import { extractVariables, showError, tryParseParams } from "~/util/helpers";
import {
iconAutoFix,
iconDollar,
iconServer,
iconStar,
iconText,
} from "~/util/icons";
import { iconAutoFix, iconDollar, iconServer, iconStar, iconText } from "~/util/icons";
import { formatQuery, formatValue, validateQuery } from "~/util/surrealql";

export interface QueryPaneProps {
Expand Down Expand Up @@ -74,9 +69,7 @@ export function QueryPane({
try {
const query = hasSelection
? activeTab.query.slice(0, selection.from) +
formatQuery(
activeTab.query.slice(selection.from, selection.to),
) +
formatQuery(activeTab.query.slice(selection.from, selection.to)) +
activeTab.query.slice(selection.to)
: formatQuery(activeTab.query);

Expand All @@ -102,9 +95,7 @@ export function QueryPane({
const query = activeTab.query;
const currentVars = tryParseParams(activeTab.variables);
const currentKeys = Object.keys(currentVars);
const variables = extractVariables(query).filter(
(v) => !currentKeys.includes(v),
);
const variables = extractVariables(query).filter((v) => !currentKeys.includes(v));

const newVars = variables.reduce(
(acc, v) => {
Expand All @@ -127,7 +118,7 @@ export function QueryPane({
});

const resolveVariables = useStable(() => {
return Object.keys(tryParseParams(activeTab.variables))
return Object.keys(tryParseParams(activeTab.variables));
});

const setSelection = useDebouncedFunction(onSelectionChange, 50);
Expand Down Expand Up @@ -156,9 +147,7 @@ export function QueryPane({
</ActionIcon>
</Tooltip>

<Tooltip
label={`Format ${hasSelection ? "selection" : "query"}`}
>
<Tooltip label={`Format ${hasSelection ? "selection" : "query"}`}>
<ActionIcon
onClick={handleFormat}
variant="light"
Expand All @@ -174,7 +163,10 @@ export function QueryPane({
label={
<Stack gap={4}>
<Text>Infer variables from query</Text>
<Text c="dimmed" size="sm">
<Text
c="dimmed"
size="sm"
>
Automatically add missing variables.
</Text>
</Stack>
Expand All @@ -189,21 +181,11 @@ export function QueryPane({
</ActionIcon>
</Tooltip>

<Tooltip
label={
showVariables
? "Hide variables"
: "Show variables"
}
>
<Tooltip label={showVariables ? "Hide variables" : "Show variables"}>
<ActionIcon
onClick={toggleVariables}
variant="light"
aria-label={
showVariables
? "Hide variables"
: "Show variables"
}
aria-label={showVariables ? "Hide variables" : "Show variables"}
>
<Icon path={iconDollar} />
</ActionIcon>
Expand Down
Loading

0 comments on commit a683897

Please sign in to comment.