Skip to content

Commit 3bb448e

Browse files
authored
Language Server Refactor (#222)
* Move language server into CadenceContainer * Update code to catch language server state * Refactor methods * Fix language callbacks bindings * More updates to server and setup * Listen for changes in deployed contracts and restart server if there is any * Add unique keys to items * Clean console.log calls * Clean up according to comments * Update language server package * Update wasm_exec
1 parent 10c1b1b commit 3bb448e

File tree

9 files changed

+271
-155
lines changed

9 files changed

+271
-155
lines changed

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@apollo/react-hooks": "^3.1.3",
1414
"@emotion/core": "^10.0.35",
1515
"@emotion/styled": "^10.0.27",
16-
"@onflow/cadence-language-server": "^0.18.1",
16+
"@onflow/cadence-language-server": "^0.20.2",
1717
"@reach/router": "^1.3.4",
1818
"@types/file-saver": "^2.0.1",
1919
"apollo-cache-inmemory": "^1.6.6",

src/components/Arguments/components.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ const renderMessage = (message: string) => {
104104
let spanClass = getSpanClass(message);
105105

106106
const { items } = message.split(' ').reduce(
107-
(acc, item) => {
107+
(acc, item,i) => {
108108
let current = acc.items[acc.items.length - 1];
109109
if (acc.startNew) {
110110
acc.startNew = false;
@@ -115,7 +115,7 @@ const renderMessage = (message: string) => {
115115
if (item.startsWith('`')) {
116116
acc.startNew = true;
117117
const span = (
118-
<span className={spanClass}>{item.replace(/`/g, '')}</span>
118+
<span className={spanClass} key={`${item}-${i}`}>{item.replace(/`/g, '')}</span>
119119
);
120120
acc.items.push(span);
121121
acc.startNew = true;
@@ -148,7 +148,7 @@ export const ErrorsList: React.FC<ErrorListProps> = (props) => {
148148
const message = renderMessage(item.message);
149149
return (
150150
<SingleError
151-
key={`${i}-${item.message}`}
151+
key={`error-${item.message}`}
152152
onClick={() => goTo(item.position)}
153153
onMouseOver={() => hover(item.highlight)}
154154
onMouseOut={() => hideDecorations()}

src/components/Arguments/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ const Arguments: React.FC<ArgumentsProps> = (props) => {
222222
project,
223223
active,
224224
isSavingCode,
225-
lastSigners
225+
lastSigners,
226+
// updateAccountDeployedCode
226227
} = useProject();
227228

228229
const [notifications, setNotifications] = useState< { [identifier: string]: string[] } >({});

src/components/CadenceEditor.tsx

Lines changed: 44 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from 'react';
22
import styled from '@emotion/styled';
33
import { keyframes } from '@emotion/core';
44
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
5-
const { MonacoServices } = require('monaco-languageclient/lib/monaco-services');
65

76
import configureCadence, { CADENCE_LANGUAGE_ID } from 'util/cadence';
87
import {
@@ -108,18 +107,16 @@ type EditorState = {
108107
viewState: any;
109108
};
110109

111-
let monacoServicesInstalled = false;
112-
113-
type CodeGetter = (index: number) => string | undefined;
114-
115110
type CadenceEditorProps = {
111+
type: EntityType;
116112
code: string;
117113
mount: string;
118114
show: boolean;
119115
onChange: any;
120116
activeId: string;
121-
type: EntityType;
122-
getCode: CodeGetter;
117+
languageServer: CadenceLanguageServer
118+
callbacks: Callbacks
119+
serverReady: boolean
123120
};
124121

125122
type CadenceEditorState = {
@@ -144,7 +141,9 @@ class CadenceEditor extends React.Component<
144141
onChange: any;
145142
activeId: string;
146143
type: EntityType;
147-
getCode: CodeGetter;
144+
languageServer: any;
145+
callbacks: Callbacks;
146+
serverReady: boolean;
148147
}) {
149148
super(props);
150149

@@ -164,73 +163,38 @@ class CadenceEditor extends React.Component<
164163
}
165164

166165
async componentDidMount() {
167-
const editor = monaco.editor.create(
168-
document.getElementById(this.props.mount),
169-
{
170-
theme: 'vs-light',
171-
language: CADENCE_LANGUAGE_ID,
172-
minimap: {
173-
enabled: false,
166+
await this.initEditor()
167+
168+
if (this.props.serverReady) {
169+
await this.loadLanguageClient()
170+
}
171+
}
172+
173+
async initEditor(){
174+
this.editor = monaco.editor.create(
175+
document.getElementById(this.props.mount),
176+
{
177+
theme: 'vs-light',
178+
language: CADENCE_LANGUAGE_ID,
179+
minimap: {
180+
enabled: false,
181+
},
174182
},
175-
},
176183
);
177-
this.editor = editor;
178-
179184
this._subscription = this.editor.onDidChangeModelContent((event: any) => {
180185
this.props.onChange(this.editor.getValue(), event);
181186
});
182187

183188
const state = this.getOrCreateEditorState(
184-
this.props.activeId,
185-
this.props.code,
189+
this.props.activeId,
190+
this.props.code,
186191
);
187192
this.editor.setModel(state.model);
188193
this.editor.focus();
189-
190-
if (this.props.activeId && !this.callbacks) {
191-
const getCode = (index: number) => this.props.getCode(index);
192-
await this.loadLanguageServer(getCode);
193-
}
194194
}
195195

196-
private async loadLanguageServer(getCode: CodeGetter) {
197-
this.callbacks = {
198-
// The actual callback will be set as soon as the language server is initialized
199-
toServer: null,
200-
201-
// The actual callback will be set as soon as the language server is initialized
202-
onClientClose: null,
203-
204-
// The actual callback will be set as soon as the language client is initialized
205-
onServerClose: null,
206-
207-
// The actual callback will be set as soon as the language client is initialized
208-
toClient: null,
209-
210-
getAddressCode(address: string): string | undefined {
211-
const number = parseInt(address, 16);
212-
if (!number) {
213-
return;
214-
}
215-
return getCode(number - 1);
216-
},
217-
};
218-
219-
// The Monaco Language Client services have to be installed globally, once.
220-
// An editor must be passed, which is only used for commands.
221-
// As the Cadence language server is not providing any commands this is OK
222-
223-
if (!monacoServicesInstalled) {
224-
monacoServicesInstalled = true;
225-
MonacoServices.install(monaco);
226-
}
227-
228-
// Start one language server per editor.
229-
// Even though one language server can handle multiple documents,
230-
// this demonstrates this is possible and is more resilient:
231-
// if the server for one editor crashes, it does not break the other editors
232-
233-
await CadenceLanguageServer.create(this.callbacks);
196+
private async loadLanguageClient() {
197+
this.callbacks = this.props.callbacks;
234198

235199
this.languageClient = createCadenceLanguageClient(this.callbacks);
236200
this.languageClient.start();
@@ -345,13 +309,26 @@ class CadenceEditor extends React.Component<
345309

346310
async componentDidUpdate(prevProps: any) {
347311
if (this.props.activeId !== prevProps.activeId) {
348-
this.switchEditor(prevProps.activeId, this.props.activeId);
349-
this.destroyMonaco();
350-
await this.componentDidMount();
312+
await this.swapMonacoEditor(prevProps.activeId, this.props.activeId)
351313
}
314+
315+
const serverStatusChanged = this.props.serverReady !== prevProps.serverReady
316+
const activeIdChanged = this.props.activeId !== prevProps.activeId
317+
const typeChanged = this.props.type !== prevProps.type
318+
if(serverStatusChanged || activeIdChanged || typeChanged){
319+
if(this.props.callbacks.toServer !== null){
320+
await this.loadLanguageClient()
321+
}
322+
}
323+
}
324+
325+
async swapMonacoEditor(prev: any, current: any){
326+
await this.destroyMonaco();
327+
await this.initEditor();
328+
this.switchEditor(prev, current);
352329
}
353330

354-
destroyMonaco() {
331+
destroyMonaco(){
355332
if (this.editor) {
356333
this.editor.dispose();
357334
const model = this.editor.getModel();

0 commit comments

Comments
 (0)