Open azhakhan opened 1 month ago
Hi @azhakhan are you aware of this https://github.com/TypeFox/monaco-languageclient/blob/main/packages/examples/src/python/client/reactPython.tsx#L47-L49 This way you can get hold of the wrapper and do whatever you want afterwards. When you want to update editor text you can do this via wrapper. The react component is basically just a shell around the monaco-editor-wrapper.
hey @kaisalmen, thanks for the response!
i went over the methods in MonacoEditorLanguageClientWrapper
and the one that seemed most relevant for my case was updateCodeResources
, so i updated my code as:
<MonacoEditorReactComp
key={value}
userConfig={config}
style={{ height: '100%' }}
onTextChanged={onTextChanged}
onLoad={(editorWrapper: MonacoEditorLanguageClientWrapper) => {
editorWrapper.updateCodeResources({
main: { text: value, uri },
original: { text: value, uri },
});
}}
/>
my assumption here is that i need to reset main
and original
to the newly received code value.
however, the issue persists.
i also tried it with async and initAndStart
<MonacoEditorReactComp
key={`${value}_${uri}`} // unique key based on both value and uri
userConfig={config}
style={{ height: '100%' }}
onTextChanged={onTextChanged}
onLoad={async (editorWrapper: MonacoEditorLanguageClientWrapper) => {
try {
await editorWrapper.initAndStart(
config,
document.getElementById('editorContainer'),
);
editorWrapper.updateCodeResources({
main: { text: value, uri },
original: { text: value, uri },
});
} catch (error) {
console.error('Failed to initialize editor wrapper:', error);
}
}}
/>
but still getting caching issue.
am i missing something?
@azhakhan The idea is that you store the wrapper that is passed in onLoad
in your app. You don't have to call initAndStart
. If you want to update the text (from your app) you just need to call updateCodeResources
. onLoad
let's you know the content of the editor was changed (e.g. everytime you typed something in the editor).
The latest version of the react-component (not yet released, I will publish a pre-release today or tomorrow and let you know) will perform a full re-init if an updated config is passed.
I hope this helps.
hey @kaisalmen, thanks for the pointer!
i’ve made the following changes:
wrapperRef
to store reference to MonacoEditorLanguageClientWrapper
wrapperRef.current = editorWrapper
when onLoad is calledwrapperRef.current.updateCodeResources
when value changes using useEffectisEditorLoaded
flag to ensure wrapperRef.current
updated only after editor is loadedconst Editor: FC<EditorProps> = ({ value, uri, onTextChanged }) => {
const wrapperRef = useRef<MonacoEditorLanguageClientWrapper | null>(null);
const [isEditorLoaded, setIsEditorLoaded] = useState(false);
const [config, setConfig] = useState(() => CreateUserConfig({ text: value, uri }));
useEffect(() => {
setConfig(CreateUserConfig({ text: value, uri }));
}, [value, uri]);
useEffect(() => {
if (isEditorLoaded && wrapperRef.current) {
console.log('WRAPPERREF.CURRENT', wrapperRef.current);
wrapperRef.current.updateCodeResources({
main: { text: value, uri },
original: { text: value, uri },
});
}
}, [isEditorLoaded, value, uri]);
const handleLoad = useCallback(async (editorWrapper: MonacoEditorLanguageClientWrapper) => {
console.log('EDITORWRAPPER', editorWrapper);
wrapperRef.current = editorWrapper;
setIsEditorLoaded(true);
}, []);
return (
<Stack style={{ height: '100%' }} key={`${uri}-editor`} pt={4}>
<MonacoEditorReactComp
key={`${uri}-editor`}
userConfig={config}
style={{ height: '100%' }}
onTextChanged={onTextChanged}
onLoad={handleLoad}
/>
</Stack>
);
};
i can confirm that editorWrapper
and wrapperRef.current
have the same values
i also confirmed that wrapperRef.current
has the right data in editorApp.config.codeResources.main.text
and the right MonacoEditorLanguageClientWrapper
(id 10 in this case) is mounted to the Editor
however, the old value is still shown in the monaco editor (see options in Select, only shown instead of 3)
not sure what else to try here.
i'll wait for the latest version of the react-component and try again.
thanks again for helping me with this!
added a button to manually call updateCodeResources and it updates the content of the editor
const handleClick = () => {
wrapperRef.current.updateCodeResources({
main: { text: 'updated code', uri },
original: { text: 'updated code', uri },
});
};
return (
<Stack style={{ height: '100%' }} key={`${uri}-editor`} pt={4}>
<Button onClick={handleClick} variant="outline">
click me
</Button>
<MonacoEditorReactComp
key={`${uri}-editor`} // ensure key is unique to forcefully re-render when URI changes
userConfig={config}
style={{ height: '100%' }}
onTextChanged={onTextChanged}
onLoad={handleLoad}
/>
</Stack>
but the same updateCodeResources in useEffect does not work
@azhakhan https://www.npmjs.com/package/@typefox/monaco-editor-react/v/6.0.0-next.1 is now available. This is a pre-release and introduces some changes to the wrapper configuration.
For reference take a look at the updated python example: https://github.com/TypeFox/monaco-languageclient/blob/main/packages/examples/src/python/client/reactPython.tsx https://github.com/TypeFox/monaco-languageclient/blob/main/packages/examples/src/python/client/config.ts
hey @kaisalmen, thanks for the update!
unfortunately im still running into the same issues
i tried updating to 6.0.0-next.1 version. i’ve updated the following in package.json:
"@typefox/monaco-editor-react": "6.0.0-next.1",
"monaco-editor-wrapper": "6.0.0-next.1",
"monaco-languageclient": "9.0.0-next.1",
"vscode-languageclient": "~9.0.1",
"@codingame/monaco-vscode-keybindings-service-override": "^9.0.3",
"@codingame/monaco-vscode-python-default-extension": "^9.0.3",
i’m using python-lsp-server for lsp, so here is how i setup config:
export const createUserConfig = (
workspaceRoot: string,
code: string,
codeUri: string,
): WrapperConfig => {
const url = createUrl({ url: LSP_URL });
const webSocket = new WebSocket(url);
const iWebSocket = toSocket(webSocket);
const reader = new WebSocketMessageReader(iWebSocket);
const writer = new WebSocketMessageWriter(iWebSocket);
return {
languageClientConfigs: {
python: {
languageId: 'python',
name: 'Python Language Server',
connection: {
options: {
$type: 'WebSocketDirect',
webSocket,
},
messageTransports: { reader, writer },
},
clientOptions: {
documentSelector: ['python'],
workspaceFolder: {
index: 0,
name: 'workspace',
uri: vscode.Uri.parse(workspaceRoot),
},
},
},
},
// logLevel: LogLevel.Info,
serviceConfig: {
userServices: {
...getEditorServiceOverride(useOpenEditorStub),
...getKeybindingsServiceOverride(),
},
},
editorAppConfig: {
$type: 'extended',
codeResources: {
main: {
text: code,
uri: codeUri,
},
original: {
text: code,
uri: codeUri,
},
},
userConfiguration: {
json: JSON.stringify({
'workbench.colorTheme': 'Default Light Modern',
'editor.guides.bracketPairsHorizontal': 'active',
'editor.wordBasedSuggestions': 'off',
'editor.experimental.asyncTokenization': true,
}),
},
useDiffEditor: false,
monacoWorkerFactory: configureMonacoWorkers,
},
};
};
to be honest, i don’t fully understand what messageTransports: { reader, writer }
is for, but it’s not breaking anything, so i kept it
and here is how i initiate MonacoEditorReactComp
export const FunctionEditor = () => {
const [fileName, setSelectedFile] = useState('main.py');
const { code, refetch, isFetching, isLoading, isRefetching } = useFile();
const { cwd } = useGetPage();
const workspaceRoot = `${cwd}/workspace/${appName}/${pageName}`;
const codeUri = `${workspaceRoot}/${fileName}`;
const wrapperRef = useRef<MonacoEditorLanguageClientWrapper | null>(null);
const [tabIndex, setTabIndex] = useState(0);
const codeRef = useRef(code);
codeRef.current = code;
useEffect(() => {
if (wrapperRef.current) {
wrapperRef.current.updateCodeResources({
main: { text: code, uri: codeUri },
original: { text: code, uri: codeUri },
});
}
}, [code, codeUri]);
...
const handleSave = useCallback(() => {
const codeValue = wrapperRef.current?.getTextContents()?.text || '';
savePythonMutation.mutate({ pageName, appName, fileName, code: codeValue });
}, [appName, pageName, fileName, savePythonMutation]);
...
const memoizedWrapperConfig = useMemo(
() => createUserConfig(workspaceRoot, code, codeUri),
[workspaceRoot, code, codeUri],
);
const handleLoad = useCallback(async (editorWrapper: MonacoEditorLanguageClientWrapper) => {
wrapperRef.current = editorWrapper;
}, []);
return (
<Stack h="full" bg="white" spacing="0" w="full">
<Tabs>
<TabList mt={2} px={4}>
<Tab>main.py</Tab>
<Tab>functions.py</Tab>
<Spacer />
</TabList>
<TabPanels h="calc(100% - 50px)" w="full">
<MonacoEditorReactComp
style={{ height: '100%' }}
wrapperConfig={memoizedWrapperConfig}
onLoad={handleLoad}
/>
</TabPanels>
</Tabs>
</Stack>
);
};
i decided to ditch onTextChanged
for edits tracking and instead access updated code directly via wrapperRef.current?.getTextContents()?.text
but im still running into the same issues as before:
when we the code is updated from outside of monaco and updates are passed to the editor via updateCodeResources
, those changes are not taking place. i confirmed that updateCodeResources
is called from within useEffect, but it seems like something else is overwriting the content of the editor from within MonacoEditorReactComp
before when i had onTextChanged
implemented, i saw how right after updateCodeResources
was called, onTextChanged
was also called but with an old code.
also, because i need to edit 2 files (main.py and functions.py), i need 2 monaco editors. but if i keep code
and uri
in createUserConfig
, a new web socket is opened every time i switch between editors. but if i initiate wrapperConfig without code
and uri
and instead set them via updateCodeResources
after MonacoEditorReactComp is initiated, no code is displayed in the editor
@azhakhan can you share a reproducible example in a repo or can you augment a react example in this repo so your problem can be reproduced? Thank you.
i use monaco-languageclient to render python code editor
i'm using
MonacoEditorReactComp
and configured it as:useConfigureMonacoWorkers
is copied straight from the docsCreateUserConfig
i configureLSP
(websocket url),text
(which is code pulled form the server),uri
(full path to the file), andgetKeybindingsServiceOverride
onTextChanged
is passed toMonacoEditorReactComp
and it handles text changes in parent componentPythonEditor
(parent component), i have 2 key pieces:code
- the original code pulled from the serverupdatedCode
- a updated version of thecode
(updated viahandleTextChanged
based on changes fromMonacoEditorReactComp
) to be used by parent componenthere is how it looks in the code:
besides @typefox and @codingame packages, im using chakra ui, react query, and jotai.
editor works if only editor is used.
however, if the
code
is updated by some other components whileMonacoEditorReactComp
has its own changes (unsaved or even saved), even ifuseFile
pulls a new version of thecode
and passes it toMonacoEditorReactComp
,MonacoEditorReactComp
will not update its text content (text).i can confirm that
Editor
receives a new code withvalue={code}
and thatconfig
is contains the latestcode
inconfig.wrapperConfig.editorAppConfig.codeResources.main.text
.i tried to trigger a new render of
Editor
by passing newcode
as new key, but no luck.it is as if
MonacoEditorReactComp
is cached and won’t update it even if newconfig
received.how can i resolve this?
thanks!