Skip to content

Instantly share code, notes, and snippets.

@loopmode
Last active July 29, 2021 12:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save loopmode/0fba6494c765235b51cad86103eabce1 to your computer and use it in GitHub Desktop.
Save loopmode/0fba6494c765235b51cad86103eabce1 to your computer and use it in GitHub Desktop.
SQL editor with formatting, based on monaco-editor, plus material UI wrapper
import React from 'react';
import Editor from '@monaco-editor/react';
import { format } from 'sql-formatter';
export type InputEvent = { target: { name: string; value: string } };
export default function MonacoSqlInput({
value,
name = '',
height = 500,
onChange,
}: {
name?: string;
value?: string;
height?: number;
onChange?: (event: InputEvent) => void;
}) {
const handleChange = React.useCallback(
(value) => {
onChange?.({
target: { name, value },
});
},
[onChange, name]
);
const handleMount = React.useCallback(
(editor, monaco) => initSqlFormatter(monaco),
[]
);
return (
<Editor
language="sql"
value={value || ''}
height={height}
onChange={handleChange}
onMount={handleMount}
/>
);
}
export function initSqlFormatter(monaco: typeof import('monaco-editor')) {
const m = monaco as any;
if (m.__sqlFormatterInitialized) return;
m.__sqlFormatterInitialized = true;
//--------------------------------------------------------------------------------
// based on https://stackoverflow.com/a/66344338/368254
//--------------------------------------------------------------------------------
// define a document formatting provider
// then you contextmenu will add an "Format Document" action
const docFormatter = monaco.languages.registerDocumentFormattingEditProvider(
'sql',
{
provideDocumentFormattingEdits(model, options) {
var formatted = format(model.getValue(), {
indent: ' '.repeat(options.tabSize),
});
return [
{
range: model.getFullModelRange(),
text: formatted,
},
];
},
}
);
// define a range formatting provider
// select some codes and right click those codes
// you contextmenu will have an "Format Selection" action
const rangeFormatter =
monaco.languages.registerDocumentRangeFormattingEditProvider('sql', {
provideDocumentRangeFormattingEdits(model, range, options) {
var formatted = format(model.getValueInRange(range), {
indent: ' '.repeat(options.tabSize),
});
return [
{
range: range,
text: formatted,
},
];
},
});
return {
dispose: () => {
docFormatter.dispose();
rangeFormatter.dispose();
},
};
}
// material-ui wrapper
import React from 'react';
import { Box, InputBaseComponentProps, TextField } from '@material-ui/core';
const MonacoSqlInput = React.lazy(() => import('./MonacoSqlInput'));
type BaseProps = Omit<
React.ComponentProps<typeof TextField>,
'multiline' | 'rows'
>;
export const SqlTextField = React.memo(function SqlTextField({
onChange,
InputProps,
height,
...props
}: BaseProps & {
height?: number;
}) {
return (
<TextField
{...props}
multiline
InputProps={React.useMemo(
() =>
Object.assign({}, InputProps, {
inputComponent: (
inputComponentProps: React.PropsWithChildren<InputBaseComponentProps>
) => {
return (
<Box mt={2} width={'100%'}>
<React.Suspense fallback={<div />}>
<MonacoSqlInput
{...inputComponentProps}
onChange={onChange as any}
height={height}
/>
</React.Suspense>
</Box>
);
},
}),
[InputProps, onChange, height]
)}
/>
);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment