Home / Function/ MonacoEditor() — supabase Function Reference

MonacoEditor() — supabase Function Reference

Architecture documentation for the MonacoEditor() function in MonacoEditor.tsx from the supabase codebase.

Entity Profile

Dependency Diagram

graph TD
  7314d59d_471f_cfe1_1fae_6dc8c5a6d203["MonacoEditor()"]
  2bdac7e5_a25b_6415_051d_1503de098a7f["createSqlSnippetSkeletonV2()"]
  7314d59d_471f_cfe1_1fae_6dc8c5a6d203 -->|calls| 2bdac7e5_a25b_6415_051d_1503de098a7f
  style 7314d59d_471f_cfe1_1fae_6dc8c5a6d203 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx lines 40–287

const MonacoEditor = ({
  id,
  snippetName,
  editorRef,
  monacoRef,
  autoFocus = true,
  placeholder = '',
  className,
  executeQuery,
  onHasSelection,
  onPrompt,
  onMount,
}: MonacoEditorProps) => {
  const router = useRouter()
  const { profile } = useProfile()
  const { ref, content } = useParams()
  const { data: project } = useSelectedProjectQuery()

  const snapV2 = useSqlEditorV2StateSnapshot()
  const tabsSnap = useTabsStateSnapshot()
  const aiSnap = useAiAssistantStateSnapshot()
  const { openSidebar, toggleSidebar } = useSidebarManagerSnapshot()

  const [intellisenseEnabled] = useLocalStorageQuery(
    LOCAL_STORAGE_KEYS.SQL_EDITOR_INTELLISENSE,
    true
  )
  const [isAIAssistantHotkeyEnabled] = useLocalStorageQuery<boolean>(
    LOCAL_STORAGE_KEYS.HOTKEY_SIDEBAR(SIDEBAR_KEYS.AI_ASSISTANT),
    true
  )

  // [Joshen] Lodash debounce doesn't seem to be working here, so opting to use useDebounce
  const [value, setValue] = useState('')
  const debouncedValue = useDebounce(value, 1000)

  const snippet = snapV2.snippets[id]
  const disableEdit =
    snippet?.snippet.visibility === 'project' && snippet?.snippet.owner_id !== profile?.id

  const executeQueryRef = useRef(executeQuery)
  executeQueryRef.current = executeQuery

  const handleEditorOnMount: OnMount = async (editor, monaco) => {
    editorRef.current = editor
    monacoRef.current = monaco

    const model = editorRef.current.getModel()
    if (model !== null) {
      monacoRef.current.editor.setModelMarkers(model, 'owner', [])
    }

    editor.addAction({
      id: 'run-query',
      label: 'Run Query',
      keybindings: [monaco.KeyMod.CtrlCmd + monaco.KeyCode.Enter],
      contextMenuGroupId: 'operation',
      contextMenuOrder: 0,
      run: () => {
        executeQueryRef.current()
      },
    })

    editor.addAction({
      id: 'save-query',
      label: 'Save Query',
      keybindings: [monaco.KeyMod.CtrlCmd + monaco.KeyCode.KeyS],
      contextMenuGroupId: 'operation',
      contextMenuOrder: 0,
      run: () => {
        if (snippet) snapV2.addNeedsSaving(snippet.snippet.id)
      },
    })

    editor.addAction({
      id: 'explain-code',
      label: 'Explain Code',
      contextMenuGroupId: 'operation',
      contextMenuOrder: 1,
      run: () => {
        const selectedValue = (editorRef?.current as any)
          .getModel()
          .getValueInRange((editorRef?.current as any)?.getSelection())
        openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
        aiSnap.newChat({
          name: 'Explain code section',
          sqlSnippets: [selectedValue],
          initialInput: 'Can you explain this section to me in more detail?',
        })
      },
    })

    editor.addAction({
      id: 'toggle-ai-assistant',
      label: 'Toggle AI Assistant',
      keybindings: [monaco.KeyMod.CtrlCmd + monaco.KeyCode.KeyI],
      run: () => {
        if (isAIAssistantHotkeyEnabled) {
          toggleSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
        }
      },
    })

    if (onPrompt) {
      editor.addAction({
        id: 'generate-sql',
        label: 'Generate SQL',
        keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK],
        run: () => {
          const selection = editor.getSelection()
          const model = editor.getModel()
          if (!model || !selection) return

          const allLines = model.getLinesContent()

          const startLineIndex = selection.startLineNumber - 1
          const endLineIndex = selection.endLineNumber

          const beforeSelection = allLines.slice(0, startLineIndex).join('\n') + '\n'
          const selectedText = allLines.slice(startLineIndex, endLineIndex).join('\n')
          const afterSelection = '\n' + allLines.slice(endLineIndex).join('\n')

          onPrompt({
            selection: selectedText,
            beforeSelection,
            afterSelection,
            startLineNumber: selection?.startLineNumber ?? 0,
            endLineNumber: selection?.endLineNumber ?? 0,
          })
        },
      })
    }

    editor.onDidChangeCursorSelection(({ selection }) => {
      const noSelection =
        selection.startLineNumber === selection.endLineNumber &&
        selection.startColumn === selection.endColumn
      onHasSelection(!noSelection)
    })

    if (autoFocus) {
      if (editor.getValue().length === 1) editor.setPosition({ lineNumber: 1, column: 2 })
      editor.focus()
    }

    onMount?.(editor)
  }

  function handleEditorChange(value: string | undefined) {
    tabsSnap.makeActiveTabPermanent()
    if (id && value) {
      if (!snippet && ref && profile !== undefined && project !== undefined) {
        const snippet = createSqlSnippetSkeletonV2({
          idOverride: id,
          name: snippetName,
          sql: value,
          owner_id: profile?.id,
          project_id: project?.id,
        })
        snapV2.addSnippet({ projectRef: ref, snippet })
        router.push(`/project/${ref}/sql/${snippet.id}`, undefined, { shallow: true })
      }
      setValue(value)
    }
  }

  useEffect(() => {
    if (debouncedValue.length > 0 && snippet) {
      const shouldInvalidate = snippet.snippet.isNotSavedInDatabaseYet
      snapV2.setSql({ id, sql: value, shouldInvalidate })
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [debouncedValue])

  // if an SQL query is passed by the content parameter, set the editor value to its content. This
  // is usually used for sending the user to SQL editor from other pages with SQL.
  useEffect(() => {
    if (content && content.length > 0) handleEditorChange(content)
  }, [])

  return (
    <>
      {disableEdit && (
        <Admonition
          type="default"
          className="rounded-none border-0 border-b"
          title="Read-only snippet"
          description="This snippet has been shared to the project and is only editable by the owner who created this snippet. You may duplicate this snippet into a personal copy by right clicking on the snippet and selecting “Duplicate query”."
        />
      )}
      <Editor
        className={cn(className, 'monaco-editor')}
        theme={'supabase'}
        onMount={handleEditorOnMount}
        onChange={handleEditorChange}
        defaultLanguage="pgsql"
        defaultValue={snippet?.snippet.content?.sql}
        path={id}
        options={{
          tabSize: 2,
          fontSize: 13,
          placeholder,
          lineDecorationsWidth: 0,
          readOnly: disableEdit,
          minimap: { enabled: false },
          wordWrap: 'on',
          padding: { top: 4 },
          // [Joshen] Commenting the following out as it causes the autocomplete suggestion popover
          // to be positioned wrongly somehow. I'm not sure if this affects anything though, but leaving
          // comment just in case anyone might be wondering. Relevant issues:
          // - 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,
          },
        }}
      />
    </>
  )
}

Subdomains

Frequently Asked Questions

What does MonacoEditor() do?
MonacoEditor() is a function in the supabase codebase.
What does MonacoEditor() call?
MonacoEditor() calls 1 function(s): createSqlSnippetSkeletonV2.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free