Skip to content

Commit 39ea8e1

Browse files
Merge pull request #47 from RedisInsight/feature/RI-1853_Indications_commands_Workbench
#RI-1853, #RI-1608, #RI-1882, #RI-1855
2 parents 0b29adf + feee13c commit 39ea8e1

File tree

11 files changed

+186
-54
lines changed

11 files changed

+186
-54
lines changed

redisinsight/menu.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export default class MenuBuilder {
156156
{
157157
label: 'License Terms',
158158
click() {
159-
shell.openExternal('https://github.com/RedisInsight/RedisInsight/blob/master/LICENSE');
159+
shell.openExternal('https://github.com/RedisInsight/RedisInsight/blob/main/LICENSE');
160160
},
161161
},
162162
{
@@ -256,7 +256,7 @@ export default class MenuBuilder {
256256
{
257257
label: 'License Terms',
258258
click() {
259-
shell.openExternal('https://github.com/RedisInsight/RedisInsight/blob/master/LICENSE');
259+
shell.openExternal('https://github.com/RedisInsight/RedisInsight/blob/main/LICENSE');
260260
},
261261
},
262262
{
Lines changed: 1 addition & 0 deletions
Loading

redisinsight/ui/src/components/query/Query/Query.tsx

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import React, { useContext, useEffect } from 'react'
1+
import React, { useContext, useEffect, useRef } from 'react'
22
import { useSelector } from 'react-redux'
3-
import { findIndex } from 'lodash'
4-
import { decode } from 'html-entities'
3+
import { compact, findIndex } from 'lodash'
54
import cx from 'classnames'
65
import { EuiButtonIcon, EuiText, EuiToolTip } from '@elastic/eui'
76
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
87
import MonacoEditor from 'react-monaco-editor'
9-
import { useParams } from 'react-router-dom'
108

119
import {
1210
Theme,
@@ -15,17 +13,17 @@ import {
1513
KEYBOARD_SHORTCUTS,
1614
} from 'uiSrc/constants'
1715
import {
18-
getMultiCommands,
16+
decoration,
17+
geMonacoAction,
1918
getRedisCompletionProvider,
2019
getRedisMonarchTokensProvider,
21-
removeMonacoComments,
22-
splitMonacoValuePerLines
20+
MonacoAction,
21+
Nullable,
22+
toModelDeltaDecoration
2323
} from 'uiSrc/utils'
24-
import { ThemeContext } from 'uiSrc/contexts/themeContext'
25-
import { WBQueryType } from 'uiSrc/pages/workbench/constants'
2624
import { KeyboardShortcut } from 'uiSrc/components'
25+
import { ThemeContext } from 'uiSrc/contexts/themeContext'
2726
import { appRedisCommandsSelector } from 'uiSrc/slices/app/redis-commands'
28-
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
2927

3028
import styles from './styles.module.scss'
3129

@@ -34,20 +32,26 @@ export interface Props {
3432
loading: boolean;
3533
setQueryEl: Function;
3634
setQuery: (script: string) => void;
37-
onSubmit: (query?: string, historyId?: number, type?: WBQueryType) => void;
35+
onSubmit: (query?: string) => void;
3836
onKeyDown?: (e: React.KeyboardEvent, script: string) => void;
3937
}
4038

39+
interface IEditorMount {
40+
editor: monacoEditor.editor.IStandaloneCodeEditor
41+
monaco: typeof monacoEditor
42+
}
43+
44+
let decorations: string[] = []
45+
4146
const Query = (props: Props) => {
4247
const { query = '', setQuery, onKeyDown, onSubmit, setQueryEl } = props
43-
const { instanceId = '' } = useParams<{ instanceId: string }>()
4448

4549
const {
4650
commandsArray: REDIS_COMMANDS_ARRAY,
4751
spec: REDIS_COMMANDS_SPEC
4852
} = useSelector(appRedisCommandsSelector)
49-
const editorRef = React.createRef<MonacoEditor>()
5053
const { theme } = useContext(ThemeContext)
54+
const monacoObjects = useRef<Nullable<IEditorMount>>(null)
5155
let disposeCompletionItemProvider = () => {}
5256

5357
useEffect(() =>
@@ -57,6 +61,27 @@ const Query = (props: Props) => {
5761
},
5862
[])
5963

64+
useEffect(() => {
65+
if (!monacoObjects.current) return
66+
const commands = query.split('\n')
67+
const { monaco, editor } = monacoObjects.current
68+
const notCommandRegEx = /^[\s|//]/
69+
70+
const newDecorations = compact(commands.map((command, index) => {
71+
if (!command || notCommandRegEx.test(command)) return null
72+
const lineNumber = index + 1
73+
74+
return toModelDeltaDecoration(
75+
decoration(monaco, `decoration_${lineNumber}`, lineNumber, 1, lineNumber, 1)
76+
)
77+
}))
78+
79+
decorations = editor.deltaDecorations(
80+
decorations,
81+
newDecorations
82+
)
83+
}, [query])
84+
6085
const onChange = (value: string = '') => {
6186
setQuery(value)
6287
}
@@ -65,50 +90,21 @@ const Query = (props: Props) => {
6590
onKeyDown?.(e, query)
6691
}
6792

68-
const sendEventSubmitTelemetry = (commandInit = query) => {
69-
const eventData = (() => {
70-
const commands = splitMonacoValuePerLines(commandInit)
71-
72-
const [commandLine, ...rest] = commands.map((command = '') => {
73-
const matchedCommand = REDIS_COMMANDS_ARRAY.find((commandName) =>
74-
command.toUpperCase().startsWith(commandName))
75-
return matchedCommand ?? command.split(' ')?.[0]
76-
})
77-
const multiCommands = getMultiCommands(rest)
78-
79-
const command = removeMonacoComments(decode([commandLine, multiCommands].join('\n')).trim())
80-
81-
return {
82-
command,
83-
databaseId: instanceId,
84-
multiple: multiCommands ? 'Multiple' : 'Single'
85-
}
86-
})()
87-
88-
sendEventTelemetry({
89-
event: TelemetryEvent.WORKBENCH_COMMAND_SUBMITTED,
90-
eventData
91-
})
92-
}
93-
9493
const handleSubmit = (value?: string) => {
95-
sendEventSubmitTelemetry(value)
96-
9794
onSubmit(value)
9895
}
9996

10097
const editorDidMount = (
10198
editor: monacoEditor.editor.IStandaloneCodeEditor,
10299
monaco: typeof monacoEditor
103100
) => {
101+
monacoObjects.current = { editor, monaco }
102+
104103
editor.focus()
105104
setQueryEl(editor)
106105

107106
setupMonacoRedisLang(monaco)
108-
editor.addCommand(
109-
monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
110-
() => handleSubmit(editor.getValue())
111-
)
107+
editor.addAction(geMonacoAction(MonacoAction.Submit, (editor) => handleSubmit(editor.getValue()), monaco))
112108
}
113109

114110
const setupMonacoRedisLang = (monaco: typeof monacoEditor) => {
@@ -134,6 +130,8 @@ const Query = (props: Props) => {
134130
padding: { top: 10 },
135131
automaticLayout: true,
136132
formatOnPaste: false,
133+
glyphMargin: true,
134+
lineNumbersMinChars: 4
137135
// fontFamily: 'Inconsolata',
138136
// fontSize: 16,
139137
// minimap: {
@@ -145,11 +143,11 @@ const Query = (props: Props) => {
145143
<div className={styles.container} onKeyDown={handleKeyDown} role="textbox" tabIndex={0}>
146144
<div className={styles.input} data-testid="query-input-container">
147145
<MonacoEditor
148-
ref={editorRef}
149146
language={MonacoLanguage.Redis}
150147
theme={theme === Theme.Dark ? 'vs-dark' : 'vs-light'}
151148
value={query}
152149
options={options}
150+
className={`${MonacoLanguage.Redis}-editor`}
153151
onChange={onChange}
154152
editorDidMount={editorDidMount}
155153
/>

redisinsight/ui/src/components/query/QueryWrapper.tsx

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import React from 'react'
22
import { useSelector } from 'react-redux'
33
import { EuiLoadingContent } from '@elastic/eui'
4+
import { decode } from 'html-entities'
5+
import { useParams } from 'react-router-dom'
6+
7+
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
48
import { appRedisCommandsSelector } from 'uiSrc/slices/app/redis-commands'
9+
import { getMultiCommands, removeMonacoComments, splitMonacoValuePerLines } from 'uiSrc/utils'
510
import Query from './Query'
6-
711
import styles from './Query/styles.module.scss'
812

913
export interface Props {
@@ -12,11 +16,46 @@ export interface Props {
1216
setQuery: (script: string) => void;
1317
setQueryEl: Function;
1418
onKeyDown?: (e: React.KeyboardEvent, script: string) => void;
15-
onSubmit: () => void;
19+
onSubmit: (value?: string) => void;
1620
}
1721
const QueryWrapper = (props: Props) => {
1822
const { query = '', loading, setQuery, setQueryEl, onKeyDown, onSubmit } = props
19-
const { loading: isCommandsLoading } = useSelector(appRedisCommandsSelector)
23+
const { instanceId = '' } = useParams<{ instanceId: string }>()
24+
const {
25+
loading: isCommandsLoading,
26+
commandsArray: REDIS_COMMANDS_ARRAY,
27+
} = useSelector(appRedisCommandsSelector)
28+
29+
const sendEventSubmitTelemetry = (commandInit = query) => {
30+
const eventData = (() => {
31+
const commands = splitMonacoValuePerLines(commandInit)
32+
33+
const [commandLine, ...rest] = commands.map((command = '') => {
34+
const matchedCommand = REDIS_COMMANDS_ARRAY.find((commandName) =>
35+
command.toUpperCase().startsWith(commandName))
36+
return matchedCommand ?? command.split(' ')?.[0]
37+
})
38+
const multiCommands = getMultiCommands(rest)
39+
40+
const command = removeMonacoComments(decode([commandLine, multiCommands].join('\n')).trim())
41+
42+
return {
43+
command,
44+
databaseId: instanceId,
45+
multiple: multiCommands ? 'Multiple' : 'Single'
46+
}
47+
})()
48+
49+
sendEventTelemetry({
50+
event: TelemetryEvent.WORKBENCH_COMMAND_SUBMITTED,
51+
eventData
52+
})
53+
}
54+
55+
const handleSubmit = (value?: string) => {
56+
sendEventSubmitTelemetry(value)
57+
onSubmit(value)
58+
}
2059

2160
const Placeholder = (
2261
<div className={styles.containerPlaceholder}>
@@ -34,7 +73,7 @@ const QueryWrapper = (props: Props) => {
3473
setQuery={setQuery}
3574
setQueryEl={setQueryEl}
3675
onKeyDown={onKeyDown}
37-
onSubmit={onSubmit}
76+
onSubmit={handleSubmit}
3877
/>
3978
)
4079
}

redisinsight/ui/src/pages/home/components/DatabasesListComponent/DatabasesList/DatabasesList.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {
2+
Direction,
3+
Criteria,
24
EuiBasicTableColumn,
35
EuiButton,
46
EuiFlexGroup,
@@ -44,7 +46,6 @@ function DatabasesList({
4446
}: Props) {
4547
const [columns, setColumns] = useState(first(columnVariations))
4648
const [selection, setSelection] = useState<Instance[]>([])
47-
4849
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
4950

5051
const { loading, data: instances } = useSelector(instancesSelector)
@@ -112,6 +113,19 @@ function DatabasesList({
112113
}),
113114
})
114115

116+
const onTableChange = ({ sort, page }: Criteria<Instance>) => {
117+
// calls also with page changing
118+
if (sort && !page) {
119+
sendEventSortedTelemetry(sort)
120+
}
121+
}
122+
123+
const sendEventSortedTelemetry = (sort: { field: keyof Instance; direction: Direction }) =>
124+
sendEventTelemetry({
125+
event: TelemetryEvent.CONFIG_DATABASES_DATABASE_LIST_SORTED,
126+
eventData: sort
127+
})
128+
115129
const deleteBtn = (
116130
<EuiButton
117131
onClick={onButtonClick}
@@ -197,6 +211,7 @@ function DatabasesList({
197211
sorting={{ sort }}
198212
selection={selectionValue}
199213
onWheel={onWheel}
214+
onTableChange={onTableChange}
200215
isSelectable
201216
/>
202217

redisinsight/ui/src/styles/base/_monaco.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,23 @@
1616
font-size: 1.6em;
1717
}
1818
}
19+
20+
.monaco-editor.redisLanguage-editor .suggest-icon {
21+
display: none !important;
22+
}
23+
24+
.monaco-glyph-run-command {
25+
color: var(--rsSubmitBtn);
26+
opacity: .5 !important;
27+
margin-left: 10px;
28+
29+
&::before {
30+
content: '';
31+
width: 12px;
32+
height: 12px;
33+
background-image: url('uiSrc/assets/img/play_icon.svg');
34+
background-size: contain;
35+
font-size: 16px;
36+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
37+
}
38+
}

redisinsight/ui/src/telemetry/events.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export enum TelemetryEvent {
77
CONFIG_DATABASES_MULTIPLE_DATABASES_DELETE_CLICKED = 'CONFIG_DATABASES_MULTIPLE_DATABASES_DELETE_CLICKED',
88
CONFIG_DATABASES_DATABASE_EDIT_CLICKED = 'CONFIG_DATABASES_DATABASE_EDIT_CLICKED',
99
CONFIG_DATABASES_DATABASE_EDIT_CANCELLED_CLICKED = 'CONFIG_DATABASES_DATABASE_EDIT_CANCELLED_CLICKED',
10+
CONFIG_DATABASES_DATABASE_LIST_SORTED = 'CONFIG_DATABASES_DATABASE_LIST_SORTED',
1011
CONFIG_DATABASES_OPEN_DATABASE_BUTTON_CLICKED = 'CONFIG_DATABASES_OPEN_DATABASE_BUTTON_CLICKED',
1112

1213
CONFIG_DATABASES_HOST_PORT_COPIED = 'CONFIG_DATABASES_HOST_PORT_COPIED',

redisinsight/ui/src/utils/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export * from './formatBytes'
2727
export * from './instanceModules'
2828
export * from './monacoRedisComplitionProvider'
2929
export * from './monacoRedisMonarchTokensProvider'
30+
export * from './monacoActions'
31+
export * from './monacoDecorations'
3032
export * from './handlePlatforms'
3133
export * from './plugins'
3234

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as monacoEditor from 'monaco-editor'
2+
3+
export enum MonacoAction {
4+
Submit = 'submit'
5+
}
6+
7+
export const geMonacoAction = (
8+
actionId: MonacoAction,
9+
action: (editor: monacoEditor.editor.IStandaloneCodeEditor, ...args: any[]) => void | Promise<void>,
10+
monaco: typeof monacoEditor,
11+
): monacoEditor.editor.IActionDescriptor => {
12+
if (actionId === MonacoAction.Submit) {
13+
return {
14+
id: 'submit',
15+
label: 'Run Commands',
16+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
17+
run: action
18+
}
19+
}
20+
21+
return { id: '', label: '', run: () => {} }
22+
}

0 commit comments

Comments
 (0)