Add a toggle for intellisense to the sql editor (#21643)

* Add a toggle for intellisense to the sql editor

* Cleanup

* Ensure that we dont load suggestions in browser unless intellisense is enabled

* Small fix

* Lol spelling

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Terry SuttonandJoshen Lim authored and GitHub committed 2024-02-29 08:49:33 -03:30
1 parent 79629b79f0
commit 377cb2dc7c
8 files changed
+181 -76

No files matched your search

@@ -2,16 +2,17 @@ import Editor, { Monaco, OnMount } from '@monaco-editor/react'
import { useParams } from 'common'
import { debounce } from 'lodash'
import { useRouter } from 'next/router'
import { MutableRefObject, useEffect, useRef } from 'react'
import { MutableRefObject, useEffect, useRef, useState } from 'react'
import { cn } from 'ui'
import { SqlSnippet } from 'data/content/sql-snippets-query'
import { useSelectedProject } from 'hooks'
import { useLocalStorageQuery, useSelectedProject } from 'hooks'
import { useProfile } from 'lib/profile'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import { untitledSnippetTitle } from './SQLEditor.constants'
import { IStandaloneCodeEditor } from './SQLEditor.types'
import { createSqlSnippetSkeleton } from './SQLEditor.utils'
import { LOCAL_STORAGE_KEYS } from 'lib/constants'
export type MonacoEditorProps = {
id: string
@@ -37,6 +38,11 @@ const MonacoEditor = ({
const { profile } = useProfile()
const project = useSelectedProject()
const [intellisenseEnabled] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.SQL_EDITOR_INTELLISENSE,
typeof window !== 'undefined' ? false : true
)
const snap = useSqlEditorStateSnapshot({ sync: true })
const snippet = snap.snippets[id]
@@ -138,6 +144,36 @@ const MonacoEditor = ({
// - https://github.com/microsoft/monaco-editor/issues/2229
// - https://github.com/microsoft/monaco-editor/issues/2503
// fixedOverflowWidgets: true,
suggest: {
showMethods: intellisenseEnabled,
showFunctions: intellisenseEnabled,
showConstructors: intellisenseEnabled,
showDeprecated: intellisenseEnabled,
showFields: intellisenseEnabled,
showVariables: intellisenseEnabled,
showClasses: intellisenseEnabled,
showStructs: intellisenseEnabled,
showInterfaces: intellisenseEnabled,
showModules: intellisenseEnabled,
showProperties: intellisenseEnabled,
showEvents: intellisenseEnabled,
showOperators: intellisenseEnabled,
showUnits: intellisenseEnabled,
showValues: intellisenseEnabled,
showConstants: intellisenseEnabled,
showEnums: intellisenseEnabled,
showEnumMembers: intellisenseEnabled,
showKeywords: intellisenseEnabled,
showWords: intellisenseEnabled,
showColors: intellisenseEnabled,
showFiles: intellisenseEnabled,
showReferences: intellisenseEnabled,
showFolders: intellisenseEnabled,
showTypeParameters: intellisenseEnabled,
showIssues: intellisenseEnabled,
showUsers: intellisenseEnabled,
showSnippets: intellisenseEnabled,
},
}}
/>
)
@@ -1,4 +1,10 @@
import { Button, IconHeart } from 'ui'
import {
Button,
IconHeart,
TooltipContent_Shadcn_,
TooltipTrigger_Shadcn_,
Tooltip_Shadcn_,
} from 'ui'
import { useQueryClient } from '@tanstack/react-query'
import { contentKeys } from 'data/content/keys'
import { Content, ContentData } from 'data/content/content-query'
@@ -75,23 +81,28 @@ const FavoriteButton = ({ id }: FavoriteButtonProps) => {
}
return (
<>
{isFavorite ? (
<Button
type="text"
size="tiny"
onClick={removeFavorite}
icon={<IconHeart size="tiny" fill="#48bb78" />}
/>
) : (
<Button
type="text"
size="tiny"
onClick={addFavorite}
icon={<IconHeart size="tiny" fill="gray" />}
/>
)}
</>
<Tooltip_Shadcn_>
<TooltipTrigger_Shadcn_ asChild>
{isFavorite ? (
<Button
type="text"
size="tiny"
onClick={removeFavorite}
className="px-1"
icon={<IconHeart size="tiny" fill="#48bb78" />}
/>
) : (
<Button
type="text"
size="tiny"
onClick={addFavorite}
className="px-1"
icon={<IconHeart size="tiny" fill="gray" />}
/>
)}
</TooltipTrigger_Shadcn_>
<TooltipContent_Shadcn_ side="bottom">Add to favorites</TooltipContent_Shadcn_>
</Tooltip_Shadcn_>
)
}
@@ -1,13 +1,32 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { IS_PLATFORM } from 'lib/constants'
import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants'
import { detectOS } from 'lib/helpers'
import { Button, IconAlignLeft, IconCommand, IconCornerDownLeft } from 'ui'
import {
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
IconAlignLeft,
IconCheck,
IconCommand,
IconCornerDownLeft,
IconSettings,
Toggle_Shadcn,
TooltipContent_Shadcn_,
TooltipTrigger_Shadcn_,
Tooltip_Shadcn_,
cn,
} from 'ui'
import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector'
import DatabaseSelector from 'components/ui/DatabaseSelector'
import { useSelectedProject } from 'hooks'
import { useLocalStorageQuery, useSelectedProject } from 'hooks'
import FavoriteButton from './FavoriteButton'
import SavingIndicator from './SavingIndicator'
import toast from 'react-hot-toast'
import { FileCog, Keyboard, SlidersHorizontal } from 'lucide-react'
export type UtilityActionsProps = {
id: string
@@ -29,45 +48,60 @@ const UtilityActions = ({
const os = detectOS()
const project = useSelectedProject()
const showReadReplicasUI = project?.is_read_replicas_enabled
const [intellisenseEnabled, setIntellisenseEnabled] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.SQL_EDITOR_INTELLISENSE,
typeof window !== 'undefined' ? false : true
)
return (
<>
<div className="inline-flex items-center justify-end gap-x-2">
<SavingIndicator id={id} />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="text"
className="px-1"
icon={<Keyboard size={14} className="text-foreground-light" />}
/>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48">
<DropdownMenuItem
className="justify-between"
onClick={() => {
setIntellisenseEnabled(!intellisenseEnabled)
toast.success(
`Successfully ${intellisenseEnabled ? 'disabled' : 'enabled'} intellisense. ${intellisenseEnabled ? 'Please refresh your browser for changes to take place.' : ''}`
)
}}
>
<p>Intellisense enabled</p>
{intellisenseEnabled && <IconCheck className="text-brand" />}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{IS_PLATFORM && <FavoriteButton id={id} />}
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger asChild>
<Tooltip_Shadcn_>
<TooltipTrigger_Shadcn_ asChild>
<Button
type="text"
onClick={() => prettifyQuery()}
className="px-1"
icon={<IconAlignLeft size="tiny" strokeWidth={2} className="text-gray-1100" />}
/>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow',
'border border-background',
].join(' ')}
>
<span className="text-xs text-foreground">Prettify SQL</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</TooltipTrigger_Shadcn_>
<TooltipContent_Shadcn_ side="bottom">Prettify SQL</TooltipContent_Shadcn_>
</Tooltip_Shadcn_>
<div className="flex items-center justify-between gap-x-2 mx-2">
<div className="flex items-center">
{showReadReplicasUI && <DatabaseSelector variant="connected-on-right" />}
<RoleImpersonationPopover
serviceRoleLabel="postgres"
variant={showReadReplicasUI ? 'connected-on-both' : 'connected-on-right'}
/>
<Button
onClick={() => executeQuery()}
disabled={isDisabled || isExecuting}
@@ -90,7 +124,7 @@ const UtilityActions = ({
</Button>
</div>
</div>
</>
</div>
)
}
@@ -23,17 +23,14 @@ const UtilityPanel = ({
<>
<div className="flex justify-between overflow-visible px-6 py-2 border-b">
<ResultsDropdown id={id} isExecuting={isExecuting} />
<div className="inline-flex items-center justify-end">
<UtilityActions
id={id}
isExecuting={isExecuting}
isDisabled={isDisabled}
hasSelection={hasSelection}
prettifyQuery={prettifyQuery}
executeQuery={executeQuery}
/>
</div>
<UtilityActions
id={id}
isExecuting={isExecuting}
isDisabled={isDisabled}
hasSelection={hasSelection}
prettifyQuery={prettifyQuery}
executeQuery={executeQuery}
/>
</div>
<div className="flex-1 p-0 pt-0 pb-0">
+1
View File
@@ -35,6 +35,7 @@ export const LOCAL_STORAGE_KEYS = {
DASHBOARD_HISTORY: (ref: string) => `dashboard-history-${ref}`,
UI_PREVIEW_CLS: 'supabase-ui-cls',
SQL_EDITOR_INTELLISENSE: 'supabase_sql-editor-intellisense-enabled',
SQL_EDITOR_SPLIT_SIZE: 'supabase_sql-editor-split-size',
LOG_EXPLORER_SPLIT_SIZE: 'supabase_log-explorer-split-size',
GRAPHIQL_RLS_BYPASS_WARNING: 'graphiql-rls-bypass-warning-dismissed',
+44 -20
View File
@@ -1,8 +1,8 @@
import { useRouter } from 'next/router'
import { useMonaco } from '@monaco-editor/react'
import { observer } from 'mobx-react-lite'
import { useEffect, useRef } from 'react'
import { useParams } from 'common'
import { observer } from 'mobx-react-lite'
import { useRouter } from 'next/router'
import { useEffect, useRef } from 'react'
import { useFunctionsQuery } from 'data/database/functions-query'
import { useKeywordsQuery } from 'data/database/keywords-query'
@@ -16,6 +16,8 @@ import { SQLEditorLayout } from 'components/layouts'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import getPgsqlCompletionProvider from 'components/ui/CodeEditor/Providers/PgSQLCompletionProvider'
import getPgsqlSignatureHelpProvider from 'components/ui/CodeEditor/Providers/PgSQLSignatureHelpProvider'
import { useLocalStorageQuery } from 'hooks'
import { LOCAL_STORAGE_KEYS } from 'lib/constants'
import { useAppStateSnapshot } from 'state/app-state'
import { useSnippets, useSqlEditorStateSnapshot } from 'state/sql-editor'
@@ -31,6 +33,11 @@ const SqlEditor: NextPageWithLayout = () => {
const snippets = useSnippets(ref)
const { mutateAsync: formatQuery } = useFormatQueryMutation()
const [intellisenseEnabled] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.SQL_EDITOR_INTELLISENSE,
typeof window !== 'undefined' ? false : true
)
async function formatPgsql(value: string) {
try {
if (!project) throw new Error('No project')
@@ -46,29 +53,46 @@ const SqlEditor: NextPageWithLayout = () => {
}
}
const { data: keywords, isSuccess: isKeywordsSuccess } = useKeywordsQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const { data: functions, isSuccess: isFunctionsSuccess } = useFunctionsQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const { data: schemas, isSuccess: isSchemasSuccess } = useSchemasQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const { data: tableColumns, isSuccess: isTableColumnsSuccess } = useTableColumnsQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const { data: keywords, isSuccess: isKeywordsSuccess } = useKeywordsQuery(
{
projectRef: project?.ref,
connectionString: project?.connectionString,
},
{ enabled: intellisenseEnabled }
)
const { data: functions, isSuccess: isFunctionsSuccess } = useFunctionsQuery(
{
projectRef: project?.ref,
connectionString: project?.connectionString,
},
{ enabled: intellisenseEnabled }
)
const { data: schemas, isSuccess: isSchemasSuccess } = useSchemasQuery(
{
projectRef: project?.ref,
connectionString: project?.connectionString,
},
{ enabled: intellisenseEnabled }
)
const { data: tableColumns, isSuccess: isTableColumnsSuccess } = useTableColumnsQuery(
{
projectRef: project?.ref,
connectionString: project?.connectionString,
},
{ enabled: intellisenseEnabled }
)
const pgInfoRef = useRef<any>(null)
const formatPgsqlRef = useRef(formatPgsql)
formatPgsqlRef.current = formatPgsql
const isPgInfoReady =
isTableColumnsSuccess && isSchemasSuccess && isKeywordsSuccess && isFunctionsSuccess
intellisenseEnabled &&
isTableColumnsSuccess &&
isSchemasSuccess &&
isKeywordsSuccess &&
isFunctionsSuccess
if (isPgInfoReady) {
if (pgInfoRef.current === null) {
pgInfoRef.current = {}
+2
View File
@@ -207,6 +207,8 @@ export {
TooltipContent as TooltipContent_Shadcn_,
} from './src/components/shadcn/ui/tooltip'
export { Toggle as Toggle_Shadcn } from './src/components/shadcn/ui/toggle'
export { ScrollArea, ScrollBar } from './src/components/shadcn/ui/scroll-area'
export { Separator } from './src/components/shadcn/ui/separator'
@@ -7,7 +7,7 @@ import * as React from 'react'
import { cn } from '../../../lib/utils/cn'
const toggleVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors data-[state=on]:bg-accent data-[state=on]:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background hover:bg-muted hover:text-foreground-muted',
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors data-[state=on]:bg-accent data-[state=on]:bg-surface-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background bg-surface-200 hover:bg-surface-300 px-3 py-1 h-auto text-foreground transition-all',
{
variants: {
variant: {