Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion web/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true
Expand All @@ -21,7 +22,9 @@ module.exports = {
"prettier",
"unused-imports"
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
ecmaVersion: "latest",
ecmaFeatures: {
jsx: true
Expand Down Expand Up @@ -50,7 +53,11 @@ module.exports = {
},
'import/resolver': {
typescript: {
alwaysTryTypes: true
alwaysTryTypes: true,
project: ["**/tsconfig.json"]
},
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
}
}
},
Expand Down
4 changes: 2 additions & 2 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"@typescript-eslint/eslint-plugin": "^7.16.0",
"@typescript-eslint/parser": "^7.16.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"@xterm/addon-canvas": "^0.6.0-beta.1",
"@xterm/addon-fit": "^0.9.0-beta.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const RunOutput: React.FC<StateProps & {}> = ({ status, monaco, terminal }) => {
}
}, [theme])
const fontFamily = useMemo(() => getFontFamily(monaco?.fontFamily ?? DEFAULT_FONT), [monaco])
const isClean = !status || !status?.dirty
const isClean = !status?.dirty

return (
<div className="RunOutput" style={styles}>
Expand Down
58 changes: 39 additions & 19 deletions web/src/components/features/workspace/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@ import { Spinner } from '@fluentui/react'
import MonacoEditor, { type Monaco } from '@monaco-editor/react'
import { KeyMod, KeyCode, type editor, type IKeyboardEvent } from 'monaco-editor'

import apiClient from '~/services/api'
import { createVimModeAdapter, type StatusBarAdapter, type VimModeKeymap } from '~/plugins/vim/editor'
import { Analyzer } from '~/services/analyzer'
import { TargetType } from '~/services/config'
import { Connect, newMarkerAction, runFileDispatcher } from '~/store'
import { type MonacoSettings, TargetType } from '~/services/config'
import { connect, newMarkerAction, runFileDispatcher, type StateDispatch } from '~/store'
import { type WorkspaceState, dispatchFormatFile, dispatchResetWorkspace, dispatchUpdateFile } from '~/store/workspace'
import { getTimeNowUsageMarkers, wrapAsyncWithDebounce } from '../utils'
import { attachCustomCommands } from '../commands'
import { LANGUAGE_GOLANG, stateToOptions } from '../props'
import { getTimeNowUsageMarkers, wrapAsyncWithDebounce } from './utils'
import { attachCustomCommands } from './commands'
import { LANGUAGE_GOLANG, stateToOptions } from './props'
import { configureMonacoLoader } from './loader'
import { registerGoLanguageProvider } from './autocomplete'
import type { VimState } from '~/store/vim/state'

const ANALYZE_DEBOUNCE_TIME = 500

interface CodeEditorState {
code?: string
loading?: boolean
}
// ask monaco-editor/react to use our own Monaco instance.
configureMonacoLoader()

const mapWorkspaceProps = ({ files, selectedFile }: WorkspaceState) => {
if (!selectedFile) {
Expand All @@ -33,16 +35,22 @@ const mapWorkspaceProps = ({ files, selectedFile }: WorkspaceState) => {
}
}

@Connect(({ workspace, ...s }) => ({
...mapWorkspaceProps(workspace),
darkMode: s.settings.darkMode,
vimModeEnabled: s.settings.enableVimMode,
isServerEnvironment: s.runTarget.target === TargetType.Server,
loading: s.status?.loading,
options: s.monaco,
vim: s.vim,
}))
export class CodeEditor extends React.Component<any, CodeEditorState> {
interface CodeEditorState {
code?: string
loading?: boolean
}

interface Props extends CodeEditorState {
fileName: string
darkMode: boolean
vimModeEnabled: boolean
isServerEnvironment: boolean
options: MonacoSettings
vim?: VimState | null
dispatch: StateDispatch
}

class CodeEditor extends React.Component<Props> {
private analyzer?: Analyzer
private editorInstance?: editor.IStandaloneCodeEditor
private vimAdapter?: VimModeKeymap
Expand All @@ -57,6 +65,8 @@ export class CodeEditor extends React.Component<any, CodeEditorState> {
this.editorInstance = editorInstance
this.monaco = monacoInstance

registerGoLanguageProvider(apiClient)

editorInstance.onKeyDown((e) => this.onKeyDown(e))
const [vimAdapter, statusAdapter] = createVimModeAdapter(this.props.dispatch, editorInstance)
this.vimAdapter = vimAdapter
Expand Down Expand Up @@ -221,3 +231,13 @@ export class CodeEditor extends React.Component<any, CodeEditorState> {
)
}
}

export const ConnectedCodeEditor = connect<CodeEditorState, {}>(({ workspace, ...s }) => ({
...mapWorkspaceProps(workspace),
darkMode: s.settings.darkMode,
vimModeEnabled: s.settings.enableVimMode,
isServerEnvironment: s.runTarget.target === TargetType.Server,
loading: s.status?.loading,
options: s.monaco,
vim: s.vim,
}))(CodeEditor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as monaco from 'monaco-editor'
import type { IAPIClient } from '~/services/api'

import { GoCompletionItemProvider } from './provider'

let alreadyRegistered = false
export const registerGoLanguageProvider = (client: IAPIClient) => {
if (alreadyRegistered) {
console.warn('Go Language provider was already registered')
return
}

alreadyRegistered = true
return monaco.languages.registerCompletionItemProvider('go', new GoCompletionItemProvider(client))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Matches package (and method name)
const COMPL_REGEXP = /([a-zA-Z0-9_]+)(\.([A-Za-z0-9_]+))?$/
const R_GROUP_PKG = 1
const R_GROUP_METHOD = 3

export const parseExpression = (expr: string) => {
COMPL_REGEXP.lastIndex = 0 // Reset regex state
const m = COMPL_REGEXP.exec(expr)
if (!m) {
return null
}

const varName = m[R_GROUP_PKG]
const propValue = m[R_GROUP_METHOD]

if (!propValue) {
return { value: varName }
}

return {
packageName: varName,
value: propValue,
}
}
Original file line number Diff line number Diff line change
@@ -1,52 +1,12 @@
import { loader } from '@monaco-editor/react'
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import * as monaco from 'monaco-editor'
import type * as monaco from 'monaco-editor'
import { type IAPIClient } from '~/services/api'
import { wrapAsyncWithDebounce } from '../utils'
import snippets from './snippets'
import { wrapAsyncWithDebounce } from './utils'
import { parseExpression } from './parse'

// Import aliases
type CompletionList = monaco.languages.CompletionList
type CompletionContext = monaco.languages.CompletionContext
type ITextModel = monaco.editor.ITextModel
type Position = monaco.Position
type CancellationToken = monaco.CancellationToken

let alreadyRegistered = false

// Matches package (and method name)
const COMPL_REGEXP = /([a-zA-Z0-9_]+)(\.([A-Za-z0-9_]+))?$/
const R_GROUP_PKG = 1
const R_GROUP_METHOD = 3
const SUGGESTIONS_DEBOUNCE_DELAY = 500

const parseExpression = (expr: string) => {
COMPL_REGEXP.lastIndex = 0 // Reset regex state
const m = COMPL_REGEXP.exec(expr)
if (!m) {
return null
}

const varName = m[R_GROUP_PKG]
const propValue = m[R_GROUP_METHOD]

if (!propValue) {
return { value: varName }
}

return {
packageName: varName,
value: propValue,
}
}

self.MonacoEnvironment = {
getWorker: () => {
return new EditorWorker()
},
}

class GoCompletionItemProvider implements monaco.languages.CompletionItemProvider {
export class GoCompletionItemProvider implements monaco.languages.CompletionItemProvider {
private readonly getSuggestionFunc: IAPIClient['getSuggestions']

constructor(private readonly client: IAPIClient) {
Expand All @@ -57,11 +17,11 @@ class GoCompletionItemProvider implements monaco.languages.CompletionItemProvide
}

async provideCompletionItems(
model: ITextModel,
position: Position,
context: CompletionContext,
token: CancellationToken,
): Promise<CompletionList> {
model: monaco.editor.ITextModel,
position: monaco.Position,
context: monaco.languages.CompletionContext,
token: monaco.CancellationToken,
): Promise<monaco.languages.CompletionList> {
const val = model
.getValueInRange({
startLineNumber: position.lineNumber,
Expand Down Expand Up @@ -108,14 +68,3 @@ class GoCompletionItemProvider implements monaco.languages.CompletionItemProvide
}
}
}

export const registerGoLanguageProvider = (client: IAPIClient) => {
if (alreadyRegistered) {
console.warn('Go Language provider was already registered')
return
}

alreadyRegistered = true
loader.config({ monaco })
return monaco.languages.registerCompletionItemProvider('go', new GoCompletionItemProvider(client))
}
20 changes: 20 additions & 0 deletions web/src/components/features/workspace/CodeEditor/loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { loader } from '@monaco-editor/react'
import * as monaco from 'monaco-editor'

import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'

let loaderConfigured = false
export const configureMonacoLoader = () => {
if (loaderConfigured) {
return
}

loader.config({ monaco })
loaderConfigured = true
}

self.MonacoEnvironment = {
getWorker: () => {
return new EditorWorker()
},
}
4 changes: 2 additions & 2 deletions web/src/components/features/workspace/Workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { TabView } from '~/components/elements/tabs/TabView'
import type { TabBarAction, TabIconStyles } from '~/components/elements/tabs/types'

import { CodeEditor } from '../CodeEditor'
import { ConnectedCodeEditor } from '../CodeEditor'
import { FlexContainer } from '../FlexContainer'
import { NewFileModal } from '../NewFileModal'
import { ContentPlaceholder } from '../ContentPlaceholder'
Expand Down Expand Up @@ -117,7 +117,7 @@ const Workspace: React.FC<Props> = ({ dispatch, files, selectedFile, snippet })
>
{tabs?.length ? (
<FlexContainer>
<CodeEditor />
<ConnectedCodeEditor />
</FlexContainer>
) : (
<ContentPlaceholder
Expand Down
3 changes: 0 additions & 3 deletions web/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { registerGoLanguageProvider } from '~/components/features/workspace/provider'
import apiClient from '~/services/api'
import * as serviceWorkerRegistration from './serviceWorkerRegistration'
import { initializeIcons } from './icons'
import { App } from './App'
Expand All @@ -12,7 +10,6 @@ import 'core-js/actual/promise/all-settled'
import 'core-js/actual/array/flat-map'

initializeIcons()
registerGoLanguageProvider(apiClient)

// eslint-disable-next-line import/no-named-as-default-member
ReactDOM.render(<App />, document.getElementById('root'))
Expand Down
11 changes: 5 additions & 6 deletions web/src/plugins/vim/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import { VimMode } from 'monaco-vim'
import VimModeKeymap from 'monaco-vim/lib/cm/keymap_vim'
import type { Nullable } from '~/utils/types'

import { runFileDispatcher } from '~/store'
import { runFileDispatcher, type StateDispatch } from '~/store'
import { dispatchShareSnippet } from '~/store/workspace'
import { type Dispatch } from '~/store/vim/state'
import {
newVimCommandDoneAction,
newVimCommandStartAction,
Expand Down Expand Up @@ -53,7 +52,7 @@ class VimModeKeymapAdapter extends VimModeKeymap {

constructor(
// "dispatch" is reserved method in inner class.
private readonly dispatchFunc: Dispatch,
private readonly dispatchFunc: StateDispatch,
editorInstance: editor.IStandaloneCodeEditor,
) {
super(editorInstance)
Expand Down Expand Up @@ -89,7 +88,7 @@ export class StatusBarAdapter {
private currentOpts?: Nullable<CommandInputOpts>

constructor(
private readonly dispatchFn: Dispatch,
private readonly dispatchFn: StateDispatch,
private readonly editor: editor.IStandaloneCodeEditor,
) {}

Expand Down Expand Up @@ -156,7 +155,7 @@ export class StatusBarAdapter {
* @param e
* @param currentData
*/
handleKeyDownEvent(e: IKeyboardEvent, currentData: string) {
handleKeyDownEvent(e: IKeyboardEvent, currentData: string = '') {
e.preventDefault()
e.stopPropagation()

Expand Down Expand Up @@ -192,7 +191,7 @@ export class StatusBarAdapter {
* @param editorInstance Monaco editor instance
*/
export const createVimModeAdapter = (
dispatch: Dispatch,
dispatch: StateDispatch,
editorInstance: editor.IStandaloneCodeEditor,
): [VimModeKeymap, StatusBarAdapter] => {
const vimAdapter: VimModeKeymap = new VimModeKeymapAdapter(dispatch, editorInstance)
Expand Down
4 changes: 4 additions & 0 deletions web/src/store/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export interface State {
terminal: TerminalState
}

/**
* Connect decorator to attach component to store.
* @deprecated use `connect` instead.
*/
export function Connect(fn: (state: State) => any) {
return function (constructor: Function) {
return connect(fn)(constructor as any) as any
Expand Down
Loading