package com.redhat.devtools.intellij.quarkus.lsp4ij; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import org.eclipse.lsp4j.CompletionParams; import org.eclipse.lsp4j.HoverParams; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextDocumentPositionParams; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.WorkspaceFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Paths; import java.util.Collections; import java.util.List; public class LSPIJUtils { private static final Logger LOGGER = LoggerFactory.getLogger(LSPIJUtils.class); @Nonnull public static List<FileType> getFileContentTypes(@Nonnull VirtualFile file) { return Collections.singletonList(file.getFileType()); } private static <T extends TextDocumentPositionParams> T toTextDocumentPositionParamsCommon(T param, int offset, Document document) { URI uri = toUri(document); Position start = toPosition(offset, document); param.setPosition(start); TextDocumentIdentifier id = new TextDocumentIdentifier(); if (uri != null) { param.setUri(uri.toString()); id.setUri(uri.toString()); } param.setTextDocument(id); return param; } public static TextDocumentPositionParams toTextDocumentPosistionParams(int offset, Document document) { return toTextDocumentPositionParamsCommon(new TextDocumentPositionParams(), offset, document); } public static HoverParams toHoverParams(int offset, Document document) { return toTextDocumentPositionParamsCommon(new HoverParams(), offset, document); } public static URI toUri(File file) { // URI scheme specified by language server protocol and LSP try { return new URI("file", "", file.getAbsoluteFile().toURI().getPath(), null); //$NON-NLS-1$ //$NON-NLS-2$ } catch (URISyntaxException e) { LOGGER.warn(e.getLocalizedMessage(), e); return file.getAbsoluteFile().toURI(); } } public static URI toUri(VirtualFile file) { return toUri(VfsUtilCore.virtualToIoFile(file)); } public static URI toUri(Document document) { VirtualFile file = getFile(document); return file != null ? toUri(file) : null; } public static VirtualFile getFile(Document document) { return FileDocumentManager.getInstance().getFile(document); } public static Document getDocument(VirtualFile docFile) { return FileDocumentManager.getInstance().getDocument(docFile); } public static Module getProject(VirtualFile file) { for (Project project : ProjectManager.getInstance().getOpenProjects()) { Module module = ProjectFileIndex.getInstance(project).getModuleForFile(file); if (module != null) { return module; } } return null; } public static int toOffset(Position start, Document document) { int lineStartOffset = document.getLineStartOffset(start.getLine()); return lineStartOffset + start.getCharacter(); } public static Position toPosition(int offset, Document document) { int line = document.getLineNumber(offset); int lineStart = document.getLineStartOffset(line); String lineTextBeforeOffset = document.getText(new TextRange(lineStart, offset)); int column = lineTextBeforeOffset.length(); return new Position(line, column); } @Nonnull public static WorkspaceFolder toWorkspaceFolder(@Nonnull Module project) { WorkspaceFolder folder = new WorkspaceFolder(); folder.setUri(toUri(project).toString()); folder.setName(project.getName()); return folder; } public static URI toUri(Module project) { File file = new File(project.getModuleFilePath()).getParentFile(); return file.toURI(); } public static void applyWorkspaceEdit(WorkspaceEdit edit) { //TODO: implements WorkspaceEdit } public static List<FileType> getDocumentContentTypes(Document document) { VirtualFile file = FileDocumentManager.getInstance().getFile(document); return Collections.singletonList(file.getFileType()); } public static VirtualFile findResourceFor(URI uri) { return LocalFileSystem.getInstance().findFileByIoFile(Paths.get(uri).toFile()); } public static VirtualFile findResourceFor(String uri) { return LocalFileSystem.getInstance().findFileByIoFile(Paths.get(uri).toFile()); } public static Editor[] editorsForFile(VirtualFile file) { Editor[] editors = new Editor[0]; Document document = FileDocumentManager.getInstance().getDocument(file); if (document != null) { editors = editorsForFile(file, document); } return editors; } public static Editor[] editorsForFile(VirtualFile file, Document document) { Module module = LSPIJUtils.getProject(file); return module!=null?EditorFactory.getInstance().getEditors(document, module.getProject()):new Editor[0]; } public static Editor editorForFile(VirtualFile file) { Editor[] editors = editorsForFile(file); return editors.length > 0 ? editors[0] : null; } public static CompletionParams toCompletionParams(URI fileUri, int offset, Document document) { Position start = toPosition(offset, document); CompletionParams param = new CompletionParams(); param.setPosition(start); param.setUri(fileUri.toString()); TextDocumentIdentifier id = new TextDocumentIdentifier(); id.setUri(fileUri.toString()); param.setTextDocument(id); return param; } public static void applyEdit(Editor editor, TextEdit textEdit, Document document) { RangeMarker marker = document.createRangeMarker(LSPIJUtils.toOffset(textEdit.getRange().getStart(), document), LSPIJUtils.toOffset(textEdit.getRange().getEnd(), document)); int startOffset = marker.getStartOffset(); int endOffset = marker.getEndOffset(); String text = textEdit.getNewText(); if (text != null) { text = text.replaceAll("\r", ""); } if (text == null || "".equals(text)) { document.deleteString(startOffset, endOffset); } else if (endOffset - startOffset <= 0) { document.insertString(startOffset, text); } else { document.replaceString(startOffset, endOffset, text); } if (text != null && !"".equals(text)) { editor.getCaretModel().moveCaretRelatively(text.length(), 0, false, false, true); } marker.dispose(); } public static void applyEdits(Editor editor, Document document, List<TextEdit> edits) { ApplicationManager.getApplication().runWriteAction(() -> edits.forEach(edit -> applyEdit(editor, edit, document))); } }