monaco-languageclient icon indicating copy to clipboard operation
monaco-languageclient copied to clipboard

Error: Unable to read file

Open buglessbuild opened this issue 1 year ago • 6 comments

Hello team,

When I try to use Peek References feature to see all the references of the function declared in the code but it throws the below error message:

Unable to read file '/workspace/file.java' (Error: Unable to resolve nonexistent file '/workspace/file.java')

I’m using [email protected] and [email protected].

Code:

import * as monaco from 'monaco-editor';
import { initServices } from 'monaco-languageclient/vscode/services';
import { MonacoLanguageClient } from 'monaco-languageclient';
import { WebSocketMessageReader, WebSocketMessageWriter, toSocket } from 'vscode-ws-jsonrpc';
import { CloseAction, ErrorAction, MessageTransports } from 'vscode-languageclient';

let editor = null;
let languageClient = null;
let webSocket = null;

const startClient = async (value, language, fileUri) => {
  await initServices({});

  editor = monaco.editor.create(editorNode, {
    minimap: {
      enabled: false,
    },
    theme: "vs-dark",
  });

  let editorModel = monaco.editor.createModel(
    value,
    language,
    monaco.Uri.parse(fileUri)
  );

  editor.setModel(editorModel);

  webSocket = initLspConnection(`ws://localhost:8080/${language}`);
};

const initLspConnection = (url, language): WebSocket => {
  webSocket = new WebSocket(url);

  webSocket.onopen = async () => {
    const socket = toSocket(webSocket);
    const reader = new WebSocketMessageReader(socket);
    const writer = new WebSocketMessageWriter(socket);
    languageClient = createLanguageClient({
      reader,
      writer,
    }, language);

    await languageClient.start();

    reader.onClose(async () => await languageClient.stop());
  };
  return webSocket;
};

const createLanguageClient = (transports, language) => {
  return new MonacoLanguageClient({
    name: "Sample Language Client",
    clientOptions: {
      workspaceFolder,
      documentSelector: [language],
      errorHandler: {
        error: () => ({ action: ErrorAction.Continue }),
        closed: () => ({ action: CloseAction.DoNotRestart }),
      },
    },
    connectionProvider: {
      get: () => {
        return Promise.resolve(transports);
      },
    },
  });
};

// calling this on language change
const onChange = async (language, fileUri, value) => {
  await dispose();

  let editorModel = monaco.editor.createModel(
    value,
    language,
    monaco.Uri.file(fileUri)
  );

  editor.setModel(editorModel);

  webSocket = initLspConnection(`ws://localhost:8080/${language}`);
};

const dispose = async () => {
  if (languageClient) {
    await languageClient.dispose();
    languageClient = null;
  }

  if (webSocket) {
    webSocket.close();
    webSocket = null;
  }
};

startClient("", 'java', myFilePath)

can you please tell me how can I enable the peek references feature? It works in official monaco editor by default but not with the monaco-vscode-api.

buglessbuild avatar Oct 14 '24 07:10 buglessbuild

Are the references in the same file?

Anyway, the issue is that you use the monaco.editor.createModel syntax to create the model, which create a standalone model not "linked" to a file on a virtual filesystem.

You better create the file on the virtualfilesystem, then use the createModelReference instead

If you want to find references in other files, every files should exist on the memory filesystem

CGNonofr avatar Oct 14 '24 08:10 CGNonofr

@CGNonofr yes, the references are in the same file. In the official monaco editor package, this feature works by using creatModel syntax so I thought it will work here as well.

buglessbuild avatar Oct 14 '24 08:10 buglessbuild

In my setup, it’s quite complicated to use async createModelReference that’s why I’m using createModel. Is there any way to make it work using createModel?

buglessbuild avatar Oct 14 '24 08:10 buglessbuild

If you want to full explanation:

Unfortunately, the model-service override is flagged as required here and I don't think you can not be using it (@kaisalmen do you confirm)?

CGNonofr avatar Oct 14 '24 09:10 CGNonofr

@CGNonofr Thanks for your explanation!

I have some more questions around createModelReference usage.

When you say 'virtual filesystem’ does that mean the file should exist on the server?

What about languages that don’t require a file to exist on the server, for example, HTML, which provides intellisense using a web worker rather than via LSP? And what if, in some cases, I don’t want to use LSP, just want to use what monaco-editor provides by default and in that case, no file will exist on server. should I still use createModelReference? What should I do in this case?

can you please help me understand when should I use createModelReference and when should I use createModel?

One more thing: why is it not recommended to use second param of createModelReference?

buglessbuild avatar Oct 15 '24 02:10 buglessbuild

When you say 'virtual filesystem’ does that mean the file should exist on the server?

What I mean by virtual filesystem is the fileSystemProvider registered in the file service. In most cases and by default, it uses a RegisteredFileSystemProvider that is empty by default and allows to registered some static files in it (with content and save callback)

By if you use a remote Language Server, it's your responsability to make sure the virtual filesystem client-side is synchronized with the filesystem accessible to the remote language server. It's the tricky part, and there is multiple ways of achieving it depending on your needs. You can for instance replace the default filesystem implementation by one of yours

What about languages that don’t require a file to exist on the server, for example, HTML, which provides intellisense using a web worker rather than via LSP?

I'm not sure what you are talking about: the VSCode html extension or the monaco worker

The HTML VSCode extension is actually using LSP, the server just runs in a webworker and the web extension makes sure the worker server has access to the VSCode virtual filesystem

The monaco worker does pretty much the same, except it relies on the list of open models using the monaco-editor api instead of the vscode virtual filesystem

And what if, in some cases, I don’t want to use LSP, just want to use what monaco-editor provides by default and in that case, no file will exist on server. should I still use createModelReference? What should I do in this case?

The monaco intellisense workers are only able to see open models, which can be fine.

The issue with createModel is you can't call it twice on the same uri, so if you create your model this way, then the intellisense ask for a references on that model, it will try to create that model once again and will crash.

As said before, the standalone implementation of the textModelService works well with that, because when a references is created, it just return an immortal references to the existing model, or throw an error

So you either need to keep the standalone textModelService, or switch to createModelReference. Keep in mind that the first one won't allow to open files not already open

I'm not sure what can prevent you from using createModelReference. I guess that's because you're storing the model in your state somewhere. The alternative is to never store models, but only the file uris, and call createModelReference only to feed the editor or play with it a bit, then release it when not required anymore

CGNonofr avatar Oct 15 '24 08:10 CGNonofr

I'm not sure what you are talking about: the VSCode html extension or the monaco worker

I’m talking about the html css workers that we import from this package: @codingame/monaco-vscode-standalone-html-language-features

If we use this worker for html then it provides the intellisense without keeping the html file in server, right? But I think references feature won’t work if I use createModel, right?

One question: I’m not able to understand from the readme when should I dispose the model reference. Should I dispose it immediately after extracting the textEditorModel from the reference as I don't need it for anything else except the editor model data. Or should I dispose it when I dispose an actual model i.e. on editor unmount?

buglessbuild avatar Oct 22 '24 12:10 buglessbuild

If we use this worker for html then it provides the intellisense without keeping the html file in server, right? But I think references feature won’t work if I use createModel, right?

It's not really about the workers. Either you enable the model service override and you have to use createModelReference, or you don't and using createModel is fine

Btw, I don't know what "the server" mean to you, we are talking about something fully client side here

One question: I’m not able to understand from the readme when should I dispose the model reference. Should I dispose it immediately after extracting the textEditorModel from the reference as I don't need it for anything else except the editor model data. Or should I dispose it when I dispose an actual model i.e. on editor unmount?

I think the name is pretty straighforward: it's a reference to the model: it need to be kept as long as you need the model. The model will be destroyed as soon as there is no reference left on it

CGNonofr avatar Oct 22 '24 13:10 CGNonofr

Okay, got it! We need to keep the model reference until we need the editor text model.

We don’t need to dispose the model separately. If we dispose all the references to that model, the model will be automatically dispose.

Thanks for the detailed answer!

buglessbuild avatar Oct 22 '24 14:10 buglessbuild

@shivam6543 are you ok to close the issue?

kaisalmen avatar Oct 25 '24 13:10 kaisalmen