feat!: Colored builds and clickable file path references

This commit is contained in:
FalsePattern 2024-03-04 16:55:25 +01:00
parent 4e401d276a
commit 66aef224b2
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
24 changed files with 595 additions and 266 deletions

View file

@ -181,6 +181,7 @@ project(":lsp") {
plugin("java-library") plugin("java-library")
} }
dependencies { dependencies {
implementation(project(":common"))
api("org.eclipse.lsp4j:org.eclipse.lsp4j:0.22.0") api("org.eclipse.lsp4j:org.eclipse.lsp4j:0.22.0")
implementation("com.vladsch.flexmark:flexmark:0.64.8") implementation("com.vladsch.flexmark:flexmark:0.64.8")
api("org.apache.commons:commons-lang3:3.14.0") api("org.apache.commons:commons-lang3:3.14.0")

View file

@ -0,0 +1,129 @@
/*
* Copyright 2023-2024 FalsePattern
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.falsepattern.zigbrains.common.util;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
public class FileUtil {
private static final Logger LOG = Logger.getInstance(FileUtil.class);
public final static String SPACE_ENCODED = "%20";
private final static OS os = (System.getProperty("os.name").toLowerCase().contains("win")) ? OS.WINDOWS : OS.UNIX;
private final static String COLON_ENCODED = "%3A";
private final static String URI_FILE_BEGIN = "file:";
private final static String URI_VALID_FILE_BEGIN = "file:///";
private final static char URI_PATH_SEP = '/';
/**
* Fixes common problems in uri, mainly related to Windows
*
* @param uri The uri to sanitize
* @return The sanitized uri
*/
public static String sanitizeURI(String uri) {
if (uri != null) {
StringBuilder reconstructed = new StringBuilder();
String uriCp = uri.replaceAll(" ", SPACE_ENCODED); //Don't trust servers
if (!uri.startsWith(URI_FILE_BEGIN)) {
LOG.warn("Malformed uri : " + uri);
return uri; //Probably not an uri
} else {
uriCp = uriCp.substring(URI_FILE_BEGIN.length());
while (uriCp.startsWith(Character.toString(URI_PATH_SEP))) {
uriCp = uriCp.substring(1);
}
reconstructed.append(URI_VALID_FILE_BEGIN);
if (os == OS.UNIX) {
return reconstructed.append(uriCp).toString();
} else {
reconstructed.append(uriCp.substring(0, uriCp.indexOf(URI_PATH_SEP)));
char driveLetter = reconstructed.charAt(URI_VALID_FILE_BEGIN.length());
if (Character.isLowerCase(driveLetter)) {
reconstructed.setCharAt(URI_VALID_FILE_BEGIN.length(), Character.toUpperCase(driveLetter));
}
if (reconstructed.toString().endsWith(COLON_ENCODED)) {
reconstructed.delete(reconstructed.length() - 3, reconstructed.length());
}
if (!reconstructed.toString().endsWith(":")) {
reconstructed.append(":");
}
return reconstructed.append(uriCp.substring(uriCp.indexOf(URI_PATH_SEP))).toString();
}
}
} else {
return null;
}
}
/**
* Returns the URI string corresponding to a VirtualFileSystem file
*
* @param file The file
* @return the URI
*/
public static String URIFromVirtualFile(VirtualFile file) {
return file == null? null : pathToUri(file.getPath());
}
/**
* Transforms an URI string into a VFS file
*
* @param uri The uri
* @return The virtual file
*/
public static VirtualFile virtualFileFromURI(URI uri) {
return LocalFileSystem.getInstance().findFileByIoFile(new File(uri));
}
/**
* Transforms an URI string into a VFS file
*
* @param uri The uri
* @return The virtual file
*/
public static VirtualFile virtualFileFromURI(String uri) {
try {
return virtualFileFromURI(new URI(sanitizeURI(uri)));
} catch (URISyntaxException e) {
LOG.warn(e);
return null;
}
}
/**
* Transforms a path into an URI string
*
* @param path The path
* @return The uri
*/
public static String pathToUri(@Nullable String path) {
return path != null ? sanitizeURI(new File(path).toURI().toString()) : null;
}
/**
* Object representing the OS type (Windows or Unix)
*/
public enum OS {
WINDOWS, UNIX
}
}

View file

@ -16,6 +16,10 @@
package com.falsepattern.zigbrains.common.util; package com.falsepattern.zigbrains.common.util;
import lombok.val;
import java.util.Arrays;
public class StringUtil { public class StringUtil {
public static String blankToNull(String value) { public static String blankToNull(String value) {
return value == null || value.isBlank() ? null : value; return value == null || value.isBlank() ? null : value;
@ -24,4 +28,56 @@ public class StringUtil {
public static String orEmpty(String value) { public static String orEmpty(String value) {
return value == null ? "" : value; return value == null ? "" : value;
} }
private static final char[] VT100_CHARS = new char[256];
static {
Arrays.fill(VT100_CHARS, ' ');
VT100_CHARS[0x6A] = '┘';
VT100_CHARS[0x6B] = '┐';
VT100_CHARS[0x6C] = '┌';
VT100_CHARS[0x6D] = '└';
VT100_CHARS[0x6E] = '┼';
VT100_CHARS[0x71] = '─';
VT100_CHARS[0x74] = '├';
VT100_CHARS[0x75] = '┤';
VT100_CHARS[0x76] = '┴';
VT100_CHARS[0x77] = '┬';
VT100_CHARS[0x78] = '│';
}
private static final String VT100_BEGIN_SEQ = "\u001B(0";
private static final String VT100_END_SEQ = "\u001B(B";
private static final int VT100_BEGIN_SEQ_LENGTH = VT100_BEGIN_SEQ.length();
private static final int VT100_END_SEQ_LENGTH = VT100_END_SEQ.length();
public static String translateVT100Escapes(String text) {
int offset = 0;
val result = new StringBuilder();
val textLength = text.length();
while (offset < textLength) {
val startIndex = text.indexOf(VT100_BEGIN_SEQ, offset);
if (startIndex < 0) {
result.append(text.substring(offset, textLength).replace(VT100_END_SEQ, ""));
break;
}
result.append(text, offset, startIndex);
val blockOffset = startIndex + VT100_BEGIN_SEQ_LENGTH;
var endIndex = text.indexOf(VT100_END_SEQ, blockOffset);
if (endIndex < 0) {
endIndex = textLength;
}
for (int i = blockOffset; i < endIndex; i++) {
val c = text.charAt(i);
if (c >= 256) {
result.append(c);
} else {
result.append(VT100_CHARS[c]);
}
}
offset = endIndex + VT100_END_SEQ_LENGTH;
}
return result.toString();
}
} }

View file

@ -15,6 +15,7 @@
*/ */
package com.falsepattern.zigbrains.lsp; package com.falsepattern.zigbrains.lsp;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus; import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition; import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition;
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper; import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
@ -331,7 +332,7 @@ public class IntellijLanguageClient {
if (wrapper.getProject() != null) { if (wrapper.getProject() != null) {
String[] extensions = wrapper.getServerDefinition().ext.split(LanguageServerDefinition.SPLIT_CHAR); String[] extensions = wrapper.getServerDefinition().ext.split(LanguageServerDefinition.SPLIT_CHAR);
for (String ext : extensions) { for (String ext : extensions) {
MutablePair<String, String> extProjectPair = new MutablePair<>(ext, FileUtils.pathToUri( MutablePair<String, String> extProjectPair = new MutablePair<>(ext, FileUtil.pathToUri(
new File(wrapper.getProjectRootPath()).getAbsolutePath())); new File(wrapper.getProjectRootPath()).getAbsolutePath()));
extToLanguageWrapper.remove(extProjectPair); extToLanguageWrapper.remove(extProjectPair);
extToServerDefinition.remove(extProjectPair); extToServerDefinition.remove(extProjectPair);

View file

@ -15,10 +15,10 @@
*/ */
package com.falsepattern.zigbrains.lsp.client; package com.falsepattern.zigbrains.lsp.client;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase; import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
import com.falsepattern.zigbrains.lsp.requests.WorkspaceEditHandler; import com.falsepattern.zigbrains.lsp.requests.WorkspaceEditHandler;
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils; import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
import com.intellij.notification.NotificationAction; import com.intellij.notification.NotificationAction;
import com.intellij.notification.NotificationGroup; import com.intellij.notification.NotificationGroup;
@ -123,7 +123,7 @@ public class DefaultLanguageClient implements LanguageClient {
@Override @Override
public void publishDiagnostics(PublishDiagnosticsParams publishDiagnosticsParams) { public void publishDiagnostics(PublishDiagnosticsParams publishDiagnosticsParams) {
String uri = FileUtils.sanitizeURI(publishDiagnosticsParams.getUri()); String uri = FileUtil.sanitizeURI(publishDiagnosticsParams.getUri());
List<Diagnostic> diagnostics = publishDiagnosticsParams.getDiagnostics(); List<Diagnostic> diagnostics = publishDiagnosticsParams.getDiagnostics();
EditorEventManagerBase.diagnostics(uri, diagnostics); EditorEventManagerBase.diagnostics(uri, diagnostics);
} }

View file

@ -15,6 +15,7 @@
*/ */
package com.falsepattern.zigbrains.lsp.client.languageserver.wrapper; package com.falsepattern.zigbrains.lsp.client.languageserver.wrapper;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient; import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
import com.falsepattern.zigbrains.lsp.client.DefaultLanguageClient; import com.falsepattern.zigbrains.lsp.client.DefaultLanguageClient;
import com.falsepattern.zigbrains.lsp.client.ServerWrapperBaseClientContext; import com.falsepattern.zigbrains.lsp.client.ServerWrapperBaseClientContext;
@ -23,7 +24,6 @@ import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.DefaultRequestManager; import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.DefaultRequestManager;
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager; import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition; import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition;
import com.falsepattern.zigbrains.lsp.editor.DocumentEventManager;
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager; import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase; import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager; import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
@ -182,7 +182,7 @@ public class LanguageServerWrapper {
} }
public static LanguageServerWrapper forVirtualFile(VirtualFile file, Project project) { public static LanguageServerWrapper forVirtualFile(VirtualFile file, Project project) {
return uriToLanguageServerWrapper.get(new ImmutablePair<>(FileUtils.VFSToURI(file), FileUtils.projectToUri(project))); return uriToLanguageServerWrapper.get(new ImmutablePair<>(FileUtil.URIFromVirtualFile(file), FileUtils.projectToUri(project)));
} }
/** /**
@ -280,7 +280,7 @@ public class LanguageServerWrapper {
return null; return null;
} }
VirtualFile currentOpenFile = selectedEditor.getFile(); VirtualFile currentOpenFile = selectedEditor.getFile();
VirtualFile requestedFile = FileUtils.virtualFileFromURI(uri); VirtualFile requestedFile = FileUtil.virtualFileFromURI(uri);
if (currentOpenFile == null || requestedFile == null) { if (currentOpenFile == null || requestedFile == null) {
return null; return null;
} }
@ -550,7 +550,7 @@ public class LanguageServerWrapper {
private InitializeParams getInitParams() throws URISyntaxException { private InitializeParams getInitParams() throws URISyntaxException {
InitializeParams initParams = new InitializeParams(); InitializeParams initParams = new InitializeParams();
String projectRootUri = FileUtils.pathToUri(projectRootPath); String projectRootUri = FileUtil.pathToUri(projectRootPath);
WorkspaceFolder workspaceFolder = new WorkspaceFolder(projectRootUri, this.project.getName()); WorkspaceFolder workspaceFolder = new WorkspaceFolder(projectRootUri, this.project.getName());
initParams.setWorkspaceFolders(Collections.singletonList(workspaceFolder)); initParams.setWorkspaceFolders(Collections.singletonList(workspaceFolder));
@ -679,7 +679,7 @@ public class LanguageServerWrapper {
List<String> connected = new ArrayList<>(); List<String> connected = new ArrayList<>();
urisUnderLspControl.forEach(s -> { urisUnderLspControl.forEach(s -> {
try { try {
connected.add(new URI(FileUtils.sanitizeURI(s)).toString()); connected.add(new URI(FileUtil.sanitizeURI(s)).toString());
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
LOG.warn(e); LOG.warn(e);
} }
@ -733,7 +733,7 @@ public class LanguageServerWrapper {
* @param projectUri The project root uri * @param projectUri The project root uri
*/ */
public void disconnect(String uri, String projectUri) { public void disconnect(String uri, String projectUri) {
uriToLanguageServerWrapper.remove(new ImmutablePair<>(FileUtils.sanitizeURI(uri), FileUtils.sanitizeURI(projectUri))); uriToLanguageServerWrapper.remove(new ImmutablePair<>(FileUtil.sanitizeURI(uri), FileUtil.sanitizeURI(projectUri)));
Set<EditorEventManager> managers = uriToEditorManagers.get(uri); Set<EditorEventManager> managers = uriToEditorManagers.get(uri);
if (managers == null) { if (managers == null) {
@ -752,7 +752,7 @@ public class LanguageServerWrapper {
} }
} }
urisUnderLspControl.remove(uri); urisUnderLspControl.remove(uri);
uriToLanguageServerWrapper.remove(new ImmutablePair<>(FileUtils.sanitizeURI(uri), FileUtils.sanitizeURI(projectUri))); uriToLanguageServerWrapper.remove(new ImmutablePair<>(FileUtil.sanitizeURI(uri), FileUtil.sanitizeURI(projectUri)));
} }
if (connectedEditors.isEmpty()) { if (connectedEditors.isEmpty()) {
stop(true); stop(true);

View file

@ -15,6 +15,7 @@
*/ */
package com.falsepattern.zigbrains.lsp.contributors.annotator; package com.falsepattern.zigbrains.lsp.contributors.annotator;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient; import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus; import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper; import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
@ -103,7 +104,7 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
VirtualFile virtualFile = file.getVirtualFile(); VirtualFile virtualFile = file.getVirtualFile();
if (FileUtils.isFileSupported(virtualFile) && IntellijLanguageClient.isExtensionSupported(virtualFile)) { if (FileUtils.isFileSupported(virtualFile) && IntellijLanguageClient.isExtensionSupported(virtualFile)) {
String uri = FileUtils.VFSToURI(virtualFile); String uri = FileUtil.URIFromVirtualFile(virtualFile);
// TODO annotations are applied to a file / document not to an editor. so store them by file and not by editor.. // TODO annotations are applied to a file / document not to an editor. so store them by file and not by editor..
EditorEventManager eventManager = EditorEventManagerBase.forUri(uri); EditorEventManager eventManager = EditorEventManagerBase.forUri(uri);
if (eventManager == null) { if (eventManager == null) {

View file

@ -15,6 +15,7 @@
*/ */
package com.falsepattern.zigbrains.lsp.contributors.symbol; package com.falsepattern.zigbrains.lsp.contributors.symbol;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient; import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus; import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager; import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
@ -70,7 +71,7 @@ public class WorkspaceSymbolProvider {
final SymbolInformation information = (result.getSymbolInformation() != null) ? final SymbolInformation information = (result.getSymbolInformation() != null) ?
result.getSymbolInformation() : from(result.getWorkspaceSymbol()); result.getSymbolInformation() : from(result.getWorkspaceSymbol());
final Location location = information.getLocation(); final Location location = information.getLocation();
final VirtualFile file = FileUtils.URIToVFS(location.getUri()); final VirtualFile file = FileUtil.virtualFileFromURI(location.getUri());
if (file != null) { if (file != null) {
final LSPIconProvider iconProviderFor = GUIUtils.getIconProviderFor(result.getDefinition()); final LSPIconProvider iconProviderFor = GUIUtils.getIconProviderFor(result.getDefinition());

View file

@ -15,6 +15,7 @@
*/ */
package com.falsepattern.zigbrains.lsp.editor; package com.falsepattern.zigbrains.lsp.editor;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.actions.LSPReferencesAction; import com.falsepattern.zigbrains.lsp.actions.LSPReferencesAction;
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerOptions; import com.falsepattern.zigbrains.lsp.client.languageserver.ServerOptions;
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager; import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
@ -25,7 +26,6 @@ import com.falsepattern.zigbrains.lsp.contributors.icon.LSPIconProvider;
import com.falsepattern.zigbrains.lsp.contributors.psi.LSPPsiElement; import com.falsepattern.zigbrains.lsp.contributors.psi.LSPPsiElement;
import com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameProcessor; import com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameProcessor;
import com.falsepattern.zigbrains.lsp.listeners.LSPCaretListenerImpl; import com.falsepattern.zigbrains.lsp.listeners.LSPCaretListenerImpl;
import com.falsepattern.zigbrains.lsp.requests.HoverHandler;
import com.falsepattern.zigbrains.lsp.requests.Timeout; import com.falsepattern.zigbrains.lsp.requests.Timeout;
import com.falsepattern.zigbrains.lsp.requests.Timeouts; import com.falsepattern.zigbrains.lsp.requests.Timeouts;
import com.falsepattern.zigbrains.lsp.requests.WorkspaceEditHandler; import com.falsepattern.zigbrains.lsp.requests.WorkspaceEditHandler;
@ -54,15 +54,10 @@ import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.editor.event.EditorMouseEvent; import com.intellij.openapi.editor.event.EditorMouseEvent;
import com.intellij.openapi.editor.event.EditorMouseListener;
import com.intellij.openapi.editor.event.EditorMouseMotionListener;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.editor.markup.HighlighterLayer;
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileEditor.OpenFileDescriptor;
@ -72,7 +67,6 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
@ -96,8 +90,6 @@ import org.eclipse.lsp4j.DocumentFormattingParams;
import org.eclipse.lsp4j.DocumentRangeFormattingParams; import org.eclipse.lsp4j.DocumentRangeFormattingParams;
import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.FormattingOptions; import org.eclipse.lsp4j.FormattingOptions;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.HoverParams;
import org.eclipse.lsp4j.InlayHint; import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.InlayHintParams; import org.eclipse.lsp4j.InlayHintParams;
import org.eclipse.lsp4j.InsertReplaceEdit; import org.eclipse.lsp4j.InsertReplaceEdit;
@ -124,7 +116,6 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.jsonrpc.messages.Tuple; import org.eclipse.lsp4j.jsonrpc.messages.Tuple;
import org.eclipse.lsp4j.services.TextDocumentService; import org.eclipse.lsp4j.services.TextDocumentService;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon; import javax.swing.Icon;
import java.awt.Point; import java.awt.Point;
@ -144,7 +135,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -370,8 +360,8 @@ public class EditorEventManager {
res.forEach(l -> { res.forEach(l -> {
Position start = l.getRange().getStart(); Position start = l.getRange().getStart();
Position end = l.getRange().getEnd(); Position end = l.getRange().getEnd();
String uri = FileUtils.sanitizeURI(l.getUri()); String uri = FileUtil.sanitizeURI(l.getUri());
VirtualFile file = FileUtils.virtualFileFromURI(uri); VirtualFile file = FileUtil.virtualFileFromURI(uri);
if (fast) { if (fast) {
if (file == null) if (file == null)
return; return;
@ -1322,7 +1312,7 @@ public class EditorEventManager {
return; return;
} }
String locUri = FileUtils.sanitizeURI(loc.getUri()); String locUri = FileUtil.sanitizeURI(loc.getUri());
if (identifier.getUri().equals(locUri) if (identifier.getUri().equals(locUri)
&& sourceOffset >= DocumentUtils.LSPPosToOffset(editor, loc.getRange().getStart()) && sourceOffset >= DocumentUtils.LSPPosToOffset(editor, loc.getRange().getStart())

View file

@ -15,6 +15,7 @@
*/ */
package com.falsepattern.zigbrains.lsp.listeners; package com.falsepattern.zigbrains.lsp.listeners;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient; import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus; import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper; import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
@ -51,7 +52,7 @@ class LSPFileEventManager {
* @param doc The document * @param doc The document
*/ */
static void willSave(Document doc) { static void willSave(Document doc) {
String uri = FileUtils.VFSToURI(FileDocumentManager.getInstance().getFile(doc)); String uri = FileUtil.URIFromVirtualFile(FileDocumentManager.getInstance().getFile(doc));
EditorEventManagerBase.willSave(uri); EditorEventManagerBase.willSave(uri);
} }
@ -72,7 +73,7 @@ class LSPFileEventManager {
if (!FileUtils.isFileSupported(file)) { if (!FileUtils.isFileSupported(file)) {
return; return;
} }
String uri = FileUtils.VFSToURI(file); String uri = FileUtil.URIFromVirtualFile(file);
if (uri == null) { if (uri == null) {
return; return;
} }
@ -96,8 +97,8 @@ class LSPFileEventManager {
return; return;
} }
String newFileUri = FileUtils.VFSToURI(file); String newFileUri = FileUtil.URIFromVirtualFile(file);
String oldParentUri = FileUtils.VFSToURI(event.getOldParent()); String oldParentUri = FileUtil.URIFromVirtualFile(event.getOldParent());
if (newFileUri == null || oldParentUri == null) { if (newFileUri == null || oldParentUri == null) {
return; return;
} }
@ -117,7 +118,7 @@ class LSPFileEventManager {
if (!FileUtils.isFileSupported(file)) { if (!FileUtils.isFileSupported(file)) {
return; return;
} }
String uri = FileUtils.VFSToURI(file); String uri = FileUtil.URIFromVirtualFile(file);
if (uri == null) { if (uri == null) {
return; return;
} }
@ -146,7 +147,7 @@ class LSPFileEventManager {
if (!FileUtils.isFileSupported(file)) { if (!FileUtils.isFileSupported(file)) {
continue; continue;
} }
String newFileUri = FileUtils.VFSToURI(file); String newFileUri = FileUtil.URIFromVirtualFile(file);
String oldFileUri = newFileUri.replace(file.getName(), oldFileName); String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
closeAndReopenAffectedFile(file, oldFileUri); closeAndReopenAffectedFile(file, oldFileUri);
} }
@ -157,7 +158,7 @@ class LSPFileEventManager {
} }
private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) { private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) {
String newFileUri = FileUtils.VFSToURI(file); String newFileUri = FileUtil.URIFromVirtualFile(file);
// Notifies the language server. // Notifies the language server.
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri, FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri,
@ -189,7 +190,7 @@ class LSPFileEventManager {
if (!FileUtils.isFileSupported(file)) { if (!FileUtils.isFileSupported(file)) {
return; return;
} }
String uri = FileUtils.VFSToURI(file); String uri = FileUtil.URIFromVirtualFile(file);
if (uri != null) { if (uri != null) {
ApplicationUtils.invokeAfterPsiEvents(() -> { ApplicationUtils.invokeAfterPsiEvents(() -> {
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri, FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,

View file

@ -15,6 +15,7 @@
*/ */
package com.falsepattern.zigbrains.lsp.requests; package com.falsepattern.zigbrains.lsp.requests;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.contributors.psi.LSPPsiElement; import com.falsepattern.zigbrains.lsp.contributors.psi.LSPPsiElement;
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager; import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase; import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
@ -81,8 +82,8 @@ public class WorkspaceEditHandler {
TextEdit edit = new TextEdit(lspRange, newName); TextEdit edit = new TextEdit(lspRange, newName);
String uri = null; String uri = null;
try { try {
uri = FileUtils.sanitizeURI( uri = FileUtil.sanitizeURI(
new URL(ui.getVirtualFile().getUrl().replace(" ", FileUtils.SPACE_ENCODED)).toURI() new URL(ui.getVirtualFile().getUrl().replace(" ", FileUtil.SPACE_ENCODED)).toURI()
.toString()); .toString());
} catch (MalformedURLException | URISyntaxException e) { } catch (MalformedURLException | URISyntaxException e) {
LOG.warn(e); LOG.warn(e);
@ -131,7 +132,7 @@ public class WorkspaceEditHandler {
TextDocumentEdit textEdit = tEdit.getLeft(); TextDocumentEdit textEdit = tEdit.getLeft();
VersionedTextDocumentIdentifier doc = textEdit.getTextDocument(); VersionedTextDocumentIdentifier doc = textEdit.getTextDocument();
int version = doc.getVersion() != null ? doc.getVersion() : Integer.MAX_VALUE; int version = doc.getVersion() != null ? doc.getVersion() : Integer.MAX_VALUE;
String uri = FileUtils.sanitizeURI(doc.getUri()); String uri = FileUtil.sanitizeURI(doc.getUri());
EditorEventManager manager = EditorEventManagerBase.forUri(uri); EditorEventManager manager = EditorEventManagerBase.forUri(uri);
if (manager != null) { if (manager != null) {
curProject[0] = manager.editor.getProject(); curProject[0] = manager.editor.getProject();
@ -151,7 +152,7 @@ public class WorkspaceEditHandler {
} else if (changes != null) { } else if (changes != null) {
changes.forEach((key, lChanges) -> { changes.forEach((key, lChanges) -> {
String uri = FileUtils.sanitizeURI(key); String uri = FileUtil.sanitizeURI(key);
EditorEventManager manager = EditorEventManagerBase.forUri(uri); EditorEventManager manager = EditorEventManagerBase.forUri(uri);
if (manager != null) { if (manager != null) {
@ -198,12 +199,12 @@ public class WorkspaceEditHandler {
Project[] projects = ProjectManager.getInstance().getOpenProjects(); Project[] projects = ProjectManager.getInstance().getOpenProjects();
//Infer the project from the uri //Infer the project from the uri
Project project = Stream.of(projects) Project project = Stream.of(projects)
.map(p -> new ImmutablePair<>(FileUtils.VFSToURI(ProjectUtil.guessProjectDir(p)), p)) .map(p -> new ImmutablePair<>(FileUtil.URIFromVirtualFile(ProjectUtil.guessProjectDir(p)), p))
.filter(p -> uri.startsWith(p.getLeft())).sorted(Collections.reverseOrder()) .filter(p -> uri.startsWith(p.getLeft())).sorted(Collections.reverseOrder())
.map(ImmutablePair::getRight).findFirst().orElse(projects[0]); .map(ImmutablePair::getRight).findFirst().orElse(projects[0]);
VirtualFile file = null; VirtualFile file = null;
try { try {
file = LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(FileUtils.sanitizeURI(uri)))); file = LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(FileUtil.sanitizeURI(uri))));
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
LOG.warn(e); LOG.warn(e);
} }

View file

@ -15,6 +15,7 @@
*/ */
package com.falsepattern.zigbrains.lsp.utils; package com.falsepattern.zigbrains.lsp.utils;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient; import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager; import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
@ -28,7 +29,6 @@ import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
@ -40,8 +40,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -56,13 +54,6 @@ import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableRe
* Various file / uri related methods * Various file / uri related methods
*/ */
public class FileUtils { public class FileUtils {
private final static OS os = (System.getProperty("os.name").toLowerCase().contains("win")) ? OS.WINDOWS : OS.UNIX;
private final static String COLON_ENCODED = "%3A";
public final static String SPACE_ENCODED = "%20";
private final static String URI_FILE_BEGIN = "file:";
private final static String URI_VALID_FILE_BEGIN = "file:///";
private final static char URI_PATH_SEP = '/';
private static final Logger LOG = Logger.getInstance(FileUtils.class); private static final Logger LOG = Logger.getInstance(FileUtils.class);
public static List<Editor> getAllOpenedEditors(Project project) { public static List<Editor> getAllOpenedEditors(Project project) {
@ -83,7 +74,7 @@ public class FileUtils {
} }
public static List<Editor> getAllOpenedEditorsForUri(@NotNull Project project, String uri) { public static List<Editor> getAllOpenedEditorsForUri(@NotNull Project project, String uri) {
VirtualFile file = virtualFileFromURI(uri); VirtualFile file = FileUtil.virtualFileFromURI(uri);
if (file == null) if (file == null)
return Collections.emptyList(); return Collections.emptyList();
return getAllOpenedEditorsForVirtualFile(project, file); return getAllOpenedEditorsForVirtualFile(project, file);
@ -106,38 +97,13 @@ public class FileUtils {
}); });
} }
/**
* This can be used to instantly apply a language server definition without restarting the IDE.
*/
public static void reloadAllEditors() {
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
for (Project project : openProjects) {
reloadEditors(project);
}
}
/**
* This can be used to instantly apply a project-specific language server definition without restarting the
* project/IDE.
*
* @param project The project instance which need to be restarted
*/
public static void reloadEditors(@NotNull Project project) {
try {
List<Editor> allOpenedEditors = FileUtils.getAllOpenedEditors(project);
allOpenedEditors.forEach(IntellijLanguageClient::editorClosed);
allOpenedEditors.forEach(IntellijLanguageClient::editorOpened);
} catch (Exception e) {
LOG.warn(String.format("Refreshing project: %s is failed due to: ", project.getName()), e);
}
}
public static Editor editorFromPsiFile(PsiFile psiFile) { public static Editor editorFromPsiFile(PsiFile psiFile) {
return editorFromVirtualFile(psiFile.getVirtualFile(), psiFile.getProject()); return editorFromVirtualFile(psiFile.getVirtualFile(), psiFile.getProject());
} }
public static Editor editorFromUri(String uri, Project project) { public static Editor editorFromUri(String uri, Project project) {
return editorFromVirtualFile(virtualFileFromURI(uri), project); return editorFromVirtualFile(FileUtil.virtualFileFromURI(uri), project);
} }
@Nullable @Nullable
@ -149,15 +115,6 @@ public class FileUtils {
return null; return null;
} }
public static VirtualFile virtualFileFromURI(String uri) {
try {
return LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(sanitizeURI(uri))));
} catch (URISyntaxException e) {
LOG.warn(e);
return null;
}
}
/** /**
* Returns a file type given an editor * Returns a file type given an editor
* *
@ -185,79 +142,14 @@ public class FileUtils {
* @return The URI * @return The URI
*/ */
public static String editorToURIString(Editor editor) { public static String editorToURIString(Editor editor) {
return sanitizeURI(VFSToURI(FileDocumentManager.getInstance().getFile(editor.getDocument()))); return FileUtil.sanitizeURI(
FileUtil.URIFromVirtualFile(FileDocumentManager.getInstance().getFile(editor.getDocument())));
} }
public static VirtualFile virtualFileFromEditor(Editor editor) { public static VirtualFile virtualFileFromEditor(Editor editor) {
return FileDocumentManager.getInstance().getFile(editor.getDocument()); return FileDocumentManager.getInstance().getFile(editor.getDocument());
} }
/**
* Returns the URI string corresponding to a VirtualFileSystem file
*
* @param file The file
* @return the URI
*/
public static String VFSToURI(VirtualFile file) {
return file == null? null : pathToUri(file.getPath());
}
/**
* Fixes common problems in uri, mainly related to Windows
*
* @param uri The uri to sanitize
* @return The sanitized uri
*/
public static String sanitizeURI(String uri) {
if (uri != null) {
StringBuilder reconstructed = new StringBuilder();
String uriCp = uri.replaceAll(" ", SPACE_ENCODED); //Don't trust servers
if (!uri.startsWith(URI_FILE_BEGIN)) {
LOG.warn("Malformed uri : " + uri);
return uri; //Probably not an uri
} else {
uriCp = uriCp.substring(URI_FILE_BEGIN.length());
while (uriCp.startsWith(Character.toString(URI_PATH_SEP))) {
uriCp = uriCp.substring(1);
}
reconstructed.append(URI_VALID_FILE_BEGIN);
if (os == OS.UNIX) {
return reconstructed.append(uriCp).toString();
} else {
reconstructed.append(uriCp.substring(0, uriCp.indexOf(URI_PATH_SEP)));
char driveLetter = reconstructed.charAt(URI_VALID_FILE_BEGIN.length());
if (Character.isLowerCase(driveLetter)) {
reconstructed.setCharAt(URI_VALID_FILE_BEGIN.length(), Character.toUpperCase(driveLetter));
}
if (reconstructed.toString().endsWith(COLON_ENCODED)) {
reconstructed.delete(reconstructed.length() - 3, reconstructed.length());
}
if (!reconstructed.toString().endsWith(":")) {
reconstructed.append(":");
}
return reconstructed.append(uriCp.substring(uriCp.indexOf(URI_PATH_SEP))).toString();
}
}
} else {
return null;
}
}
/**
* Transforms an URI string into a VFS file
*
* @param uri The uri
* @return The virtual file
*/
public static VirtualFile URIToVFS(String uri) {
try {
return LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(sanitizeURI(uri))));
} catch (URISyntaxException e) {
LOG.warn(e);
return null;
}
}
/** /**
* Returns the project base dir uri given an editor * Returns the project base dir uri given an editor
* *
@ -265,7 +157,7 @@ public class FileUtils {
* @return The project whose the editor belongs * @return The project whose the editor belongs
*/ */
public static String editorToProjectFolderUri(Editor editor) { public static String editorToProjectFolderUri(Editor editor) {
return pathToUri(editorToProjectFolderPath(editor)); return FileUtil.pathToUri(editorToProjectFolderPath(editor));
} }
public static String editorToProjectFolderPath(Editor editor) { public static String editorToProjectFolderPath(Editor editor) {
@ -275,51 +167,15 @@ public class FileUtils {
return null; return null;
} }
/**
* Transforms a path into an URI string
*
* @param path The path
* @return The uri
*/
public static String pathToUri(@Nullable String path) {
return path != null ? sanitizeURI(new File(path).toURI().toString()) : null;
}
public static String projectToUri(Project project) { public static String projectToUri(Project project) {
if (project != null && project.getBasePath() != null) { if (project != null && project.getBasePath() != null) {
return pathToUri(new File(project.getBasePath()).getAbsolutePath()); return FileUtil.pathToUri(new File(project.getBasePath()).getAbsolutePath());
} }
return null; return null;
} }
public static String documentToUri(Document document) { public static String documentToUri(Document document) {
return sanitizeURI(VFSToURI(FileDocumentManager.getInstance().getFile(document))); return FileUtil.sanitizeURI(FileUtil.URIFromVirtualFile(FileDocumentManager.getInstance().getFile(document)));
}
/**
* Object representing the OS type (Windows or Unix)
*/
public enum OS {
WINDOWS, UNIX
}
/**
* Checks if the given virtual file instance is supported by this LS client library.
*/
public static boolean isFileSupported(@Nullable VirtualFile file) {
if (file == null) {
return false;
}
if (file instanceof LightVirtualFileBase) {
return false;
}
if (file.getUrl().isEmpty() || file.getUrl().startsWith("jar:")) {
return false;
}
return IntellijLanguageClient.isExtensionSupported(file);
} }
/** /**
@ -341,6 +197,51 @@ public class FileUtils {
} }
} }
/**
* This can be used to instantly apply a language server definition without restarting the IDE.
*/
public static void reloadAllEditors() {
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
for (Project project : openProjects) {
reloadEditors(project);
}
}
/**
* This can be used to instantly apply a project-specific language server definition without restarting the
* project/IDE.
*
* @param project The project instance which need to be restarted
*/
public static void reloadEditors(@NotNull Project project) {
try {
List<Editor> allOpenedEditors = FileUtils.getAllOpenedEditors(project);
allOpenedEditors.forEach(IntellijLanguageClient::editorClosed);
allOpenedEditors.forEach(IntellijLanguageClient::editorOpened);
} catch (Exception e) {
LOG.warn(String.format("Refreshing project: %s is failed due to: ", project.getName()), e);
}
}
/**
* Checks if the given virtual file instance is supported by this LS client library.
*/
public static boolean isFileSupported(@Nullable VirtualFile file) {
if (file == null) {
return false;
}
if (file instanceof LightVirtualFileBase) {
return false;
}
if (file.getUrl().isEmpty() || file.getUrl().startsWith("jar:")) {
return false;
}
return IntellijLanguageClient.isExtensionSupported(file);
}
/** /**
* Checks if the file in editor is supported by this LS client library. * Checks if the file in editor is supported by this LS client library.
*/ */

View file

@ -0,0 +1,29 @@
/*
* Copyright 2023-2024 FalsePattern
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.falsepattern.zigbrains.project.console;
import com.intellij.execution.filters.ConsoleFilterProvider;
import com.intellij.execution.filters.Filter;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
public class ZigConsoleFilterProvider implements ConsoleFilterProvider {
@Override
public Filter @NotNull [] getDefaultFilters(@NotNull Project project) {
return new Filter[]{new ZigSourceFileFilter(project)};
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright 2023-2024 FalsePattern
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.falsepattern.zigbrains.project.console;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.project.openapi.module.ZigModuleType;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.OpenFileHyperlinkInfo;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.regex.Pattern;
@RequiredArgsConstructor
public class ZigSourceFileFilter implements Filter {
private final Project project;
@Nullable
@Override
public Result applyFilter(@NotNull String line, int entireLength) {
val lineStart = entireLength - line.length();
int splitA, splitB, splitC;
splitA = line.indexOf(':');
if (splitA < 0)
return null;
splitB = line.indexOf(':', splitA + 1);
if (splitB < 0)
return null;
splitC = line.indexOf(':', splitB + 1);
if (splitC < 0)
return null;
final int lineNumber, lineOffset;
try {
lineNumber = Math.max(Integer.parseInt(line, splitA + 1, splitB, 10) - 1, 0);
lineOffset = Math.max(Integer.parseInt(line, splitB + 1, splitC, 10) - 1, 0);
} catch (NumberFormatException ignored) {
return null;
}
val pathStr = line.substring(0, splitA);
var path = Path.of(pathStr);
if (!Files.exists(path) || !Files.isRegularFile(path)) {
val projectPath = project.getBasePath();
if (projectPath == null)
return null;
path = Path.of(projectPath, pathStr);
if (!Files.exists(path) || !Files.isRegularFile(path))
return null;
}
val file = FileUtil.virtualFileFromURI(path.toUri());
return new Result(lineStart, lineStart + splitC, new OpenFileHyperlinkInfo(project, file, lineNumber, lineOffset));
}
}

View file

@ -18,13 +18,17 @@ package com.falsepattern.zigbrains.project.execution;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.CapturingAnsiEscapesAwareProcessHandler;
import com.intellij.execution.process.CapturingProcessHandler; import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ColoredProcessHandler;
import com.intellij.execution.process.KillableColoredProcessHandler;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.util.io.BaseOutputReader; import com.intellij.util.io.BaseOutputReader;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Optional; import java.util.Optional;
public class ZigCapturingProcessHandler extends CapturingProcessHandler { public class ZigCapturingProcessHandler extends CapturingAnsiEscapesAwareProcessHandler {
public static Optional<ZigCapturingProcessHandler> startProcess(GeneralCommandLine commandLine) { public static Optional<ZigCapturingProcessHandler> startProcess(GeneralCommandLine commandLine) {
try { try {
return Optional.of(new ZigCapturingProcessHandler(commandLine)); return Optional.of(new ZigCapturingProcessHandler(commandLine));

View file

@ -20,6 +20,7 @@ import com.falsepattern.zigbrains.project.execution.ZigCapturingProcessHandler;
import com.falsepattern.zigbrains.project.runconfig.ZigProcessHandler; import com.falsepattern.zigbrains.project.runconfig.ZigProcessHandler;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.util.ProjectUtil; import com.falsepattern.zigbrains.project.util.ProjectUtil;
import com.intellij.build.BuildTextConsoleView;
import com.intellij.execution.DefaultExecutionResult; import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.CommandLineState; import com.intellij.execution.configurations.CommandLineState;
@ -31,6 +32,7 @@ import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Collections;
public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends CommandLineState { public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends CommandLineState {
protected final T configuration; protected final T configuration;
@ -42,7 +44,7 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
@Override @Override
protected @NotNull ProcessHandler startProcess() throws ExecutionException { protected @NotNull ProcessHandler startProcess() throws ExecutionException {
return new ZigCapturingProcessHandler(getCommandLine(ProjectUtil.getToolchain(getEnvironment().getProject()))); return new ZigProcessHandler(getCommandLine(ProjectUtil.getToolchain(getEnvironment().getProject())));
} }
public GeneralCommandLine getCommandLine(AbstractZigToolchain toolchain) { public GeneralCommandLine getCommandLine(AbstractZigToolchain toolchain) {
@ -63,7 +65,7 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
public DefaultExecutionResult executeCommandLine(GeneralCommandLine commandLine, ExecutionEnvironment environment) public DefaultExecutionResult executeCommandLine(GeneralCommandLine commandLine, ExecutionEnvironment environment)
throws ExecutionException { throws ExecutionException {
val handler = startProcess(commandLine); val handler = startProcess(commandLine);
val console = getConsoleBuilder().getConsole(); val console = new BuildTextConsoleView(environment.getProject(), true, Collections.emptyList());
console.attachToProcess(handler); console.attachToProcess(handler);
return new DefaultExecutionResult(console, handler); return new DefaultExecutionResult(console, handler);
} }

View file

@ -22,71 +22,133 @@ import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.ui.LabeledComponent; import com.intellij.openapi.ui.LabeledComponent;
import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.dsl.builder.AlignX; import com.intellij.ui.dsl.builder.AlignX;
import com.intellij.ui.dsl.builder.AlignY; import com.intellij.ui.dsl.builder.AlignY;
import com.intellij.ui.dsl.builder.Panel; import com.intellij.ui.dsl.builder.Panel;
import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.swing.JComponent; import javax.swing.JComponent;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import static com.intellij.ui.dsl.builder.BuilderKt.panel; import static com.intellij.ui.dsl.builder.BuilderKt.panel;
public class ZigConfigEditor<T extends ZigExecConfigBase<T>> extends SettingsEditor<T> { public class ZigConfigEditor<T extends ZigExecConfigBase<T>> extends SettingsEditor<T> {
protected final LabeledComponent<TextFieldWithBrowseButton> workingDirectoryComponent = private final List<ZigConfigModule<T>> modules;
new WorkingDirectoryComponent();
public ZigConfigEditor(List<ZigConfigModule<T>> modules) {
this.modules = new ArrayList<>(modules);
}
@Override @Override
protected void applyEditorTo(@NotNull T s) throws ConfigurationException { protected void applyEditorTo(@NotNull T s) throws ConfigurationException {
s.workingDirectory = Paths.get(workingDirectoryComponent.getComponent().getText()); for (val module: modules) {
module.applyTo(s);
}
} }
@Override @Override
protected void resetEditorFrom(@NotNull T s) { protected void resetEditorFrom(@NotNull T s) {
workingDirectoryComponent.getComponent().setText(Objects.requireNonNullElse(s.workingDirectory, "").toString()); for (val module: modules) {
module.resetFrom(s);
}
} }
@Override @Override
protected final @NotNull JComponent createEditor() { protected final @NotNull JComponent createEditor() {
return panel((p) -> { return panel((p) -> {
constructPanel(p); for (val module: modules) {
module.construct(p);
}
return null; return null;
}); });
} }
protected void constructPanel(Panel p) { public interface ZigConfigModule<T> {
void applyTo(@NotNull T s) throws ConfigurationException;
void resetFrom(@NotNull T s);
void construct(Panel p);
}
public static class WorkingDirectoryModule<T extends ZigExecConfigBase<T>> implements ZigConfigModule<T> {
protected final LabeledComponent<TextFieldWithBrowseButton> workingDirectoryComponent =
new WorkingDirectoryComponent();
@Override
public void applyTo(@NotNull T s) {
s.workingDirectory = Paths.get(workingDirectoryComponent.getComponent().getText());
}
@Override
public void resetFrom(@NotNull T s) {
workingDirectoryComponent.getComponent().setText(Objects.requireNonNullElse(s.workingDirectory, "").toString());
}
@Override
public void construct(Panel p) {
p.row(workingDirectoryComponent.getLabel(), (r) -> { p.row(workingDirectoryComponent.getLabel(), (r) -> {
r.cell(workingDirectoryComponent).resizableColumn().align(AlignX.FILL).align(AlignY.FILL); r.cell(workingDirectoryComponent).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
return null; return null;
}); });
} }
}
public static abstract class WithFilePath<T extends ZigExecConfigBase<T>> extends ZigConfigEditor<T> { public static class FilePathModule<T extends ZigExecConfigBase<T> & FilePathModule.Carrier> implements ZigConfigModule<T> {
private final ZigFilePathPanel filePathPanel = new ZigFilePathPanel(); private final ZigFilePathPanel filePathPanel = new ZigFilePathPanel();
@Override @Override
protected void applyEditorTo(@NotNull T s) throws ConfigurationException { public void applyTo(@NotNull T s) {
super.applyEditorTo(s); s.setFilePath(filePathPanel.getText());
setFilePath(s, filePathPanel.getText());
} }
@Override @Override
protected void resetEditorFrom(@NotNull T s) { public void resetFrom(@NotNull T s) {
super.resetEditorFrom(s); filePathPanel.setText(Objects.requireNonNullElse(s.getFilePath(), ""));
filePathPanel.setText(Objects.requireNonNullElse(getFilePath(s), ""));
} }
@Override @Override
protected void constructPanel(Panel p) { public void construct(Panel p) {
super.constructPanel(p);
p.row("Target file", (r) -> { p.row("Target file", (r) -> {
r.cell(filePathPanel).resizableColumn().align(AlignX.FILL).align(AlignY.FILL); r.cell(filePathPanel).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
return null; return null;
}); });
} }
protected abstract String getFilePath(T config); public interface Carrier {
protected abstract void setFilePath(T config, String path); void setFilePath(String path);
String getFilePath();
}
}
public static class ColoredModule<T extends ZigExecConfigBase<T> & ColoredModule.Carrier> implements ZigConfigModule<T> {
private final JBCheckBox checkBox = new JBCheckBox();
@Override
public void applyTo(@NotNull T s) throws ConfigurationException {
s.setColored(checkBox.isSelected());
}
@Override
public void resetFrom(@NotNull T s) {
checkBox.setSelected(s.isColored());
}
@Override
public void construct(Panel p) {
p.row("Colored terminal", (r) -> {
r.cell(checkBox);
return null;
});
}
public interface Carrier {
void setColored(boolean color);
boolean isColored();
}
} }
} }

View file

@ -16,17 +16,26 @@
package com.falsepattern.zigbrains.project.execution.base; package com.falsepattern.zigbrains.project.execution.base;
import com.falsepattern.zigbrains.project.util.ElementUtil;
import com.google.common.collect.Lists;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor; import com.intellij.execution.Executor;
import com.intellij.execution.configurations.ConfigurationFactory; import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.LocatableConfigurationBase; import com.intellij.execution.configurations.LocatableConfigurationBase;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.NlsActions; import com.intellij.openapi.util.NlsActions;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional; import java.util.Optional;
public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends LocatableConfigurationBase<ProfileStateBase<T>> { public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends LocatableConfigurationBase<ProfileStateBase<T>> {
@ -38,6 +47,30 @@ public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends
.orElse(null); .orElse(null);
} }
@Override
public @NotNull SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
return new ZigConfigEditor<>(getEditorConfigModules());
}
@Override
public void readExternal(@NotNull Element element) throws InvalidDataException {
super.readExternal(element);
ElementUtil.readString(element, "workingDirectory").ifPresent(dir -> {
try {
workingDirectory = Path.of(dir);
} catch (InvalidPathException ignored) {}
});
}
@Override
public void writeExternal(@NotNull Element element) {
super.writeExternal(element);
if (workingDirectory != null)
ElementUtil.writeString(element, "workingDirectory", workingDirectory.toString());
}
public abstract String[] buildCommandLineArgs(); public abstract String[] buildCommandLineArgs();
@Override @Override
@ -47,4 +80,7 @@ public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends
public abstract @Nullable ProfileStateBase<T> getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) public abstract @Nullable ProfileStateBase<T> getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment)
throws ExecutionException; throws ExecutionException;
public @NotNull List<ZigConfigEditor.@NotNull ZigConfigModule<T>> getEditorConfigModules() {
return new ArrayList<>(List.of(new ZigConfigEditor.WorkingDirectoryModule<>()));
}
} }

View file

@ -29,23 +29,30 @@ import com.intellij.ui.components.JBTextField;
import com.intellij.ui.dsl.builder.AlignX; import com.intellij.ui.dsl.builder.AlignX;
import com.intellij.ui.dsl.builder.AlignY; import com.intellij.ui.dsl.builder.AlignY;
import com.intellij.ui.dsl.builder.Panel; import com.intellij.ui.dsl.builder.Panel;
import lombok.Getter;
import lombok.Setter;
import lombok.val; import lombok.val;
import org.apache.groovy.util.Arrays; import org.apache.groovy.util.Arrays;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects; import java.util.Objects;
public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> { @Getter
@Setter
public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> implements
ZigConfigEditor.ColoredModule.Carrier {
public String extraArguments = ""; public String extraArguments = "";
public boolean colored = true;
public ZigExecConfigBuild(@NotNull Project project, @NotNull ConfigurationFactory factory) { public ZigExecConfigBuild(@NotNull Project project, @NotNull ConfigurationFactory factory) {
super(project, factory, "Zig Build"); super(project, factory, "Zig Build");
} }
@Override @Override
public String[] buildCommandLineArgs() { public String[] buildCommandLineArgs() {
val base = new String[]{"build"}; val base = new String[]{"build", "--color", colored ? "on" : "off"};
if (extraArguments.isBlank()) { if (extraArguments.isBlank()) {
return base; return base;
} else { } else {
@ -59,8 +66,11 @@ public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
} }
@Override @Override
public @NotNull Editor getConfigurationEditor() { public @NotNull List<ZigConfigEditor.ZigConfigModule<ZigExecConfigBuild>> getEditorConfigModules() {
return new Editor(); val arr = super.getEditorConfigModules();
arr.add(new ExtraArgsModule());
arr.add(new ZigConfigEditor.ColoredModule<>());
return arr;
} }
@Override @Override
@ -72,10 +82,8 @@ public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
public void readExternal(@NotNull Element element) throws InvalidDataException { public void readExternal(@NotNull Element element) throws InvalidDataException {
super.readExternal(element); super.readExternal(element);
val extraArguments = ElementUtil.readString(element, "extraArguments"); ElementUtil.readString(element, "extraArguments").ifPresent(x -> extraArguments = x);
if (extraArguments != null) { ElementUtil.readBoolean(element, "colored").ifPresent(x -> colored = x);
this.extraArguments = extraArguments;
}
} }
@Override @Override
@ -83,26 +91,24 @@ public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
super.writeExternal(element); super.writeExternal(element);
ElementUtil.writeString(element, "extraArguments", extraArguments); ElementUtil.writeString(element, "extraArguments", extraArguments);
ElementUtil.writeBoolean(element, "colored", colored);
} }
public static class Editor extends ZigConfigEditor<ZigExecConfigBuild> { public static class ExtraArgsModule implements ZigConfigEditor.ZigConfigModule<ZigExecConfigBuild> {
private final JBTextField extraArgs = new JBTextField(); private final JBTextField extraArgs = new JBTextField();
@Override @Override
protected void applyEditorTo(@NotNull ZigExecConfigBuild s) throws ConfigurationException { public void applyTo(@NotNull ZigExecConfigBuild s) throws ConfigurationException {
super.applyEditorTo(s);
s.extraArguments = extraArgs.getText(); s.extraArguments = extraArgs.getText();
} }
@Override @Override
protected void resetEditorFrom(@NotNull ZigExecConfigBuild s) { public void resetFrom(@NotNull ZigExecConfigBuild s) {
super.resetEditorFrom(s);
extraArgs.setText(Objects.requireNonNullElse(s.extraArguments, "")); extraArgs.setText(Objects.requireNonNullElse(s.extraArguments, ""));
} }
@Override @Override
protected void constructPanel(Panel p) { public void construct(Panel p) {
super.constructPanel(p);
p.row("Extra arguments", (r) -> { p.row("Extra arguments", (r) -> {
r.cell(extraArgs).resizableColumn().align(AlignX.FILL).align(AlignY.FILL); r.cell(extraArgs).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
return null; return null;

View file

@ -26,21 +26,26 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.InvalidDataException;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.val;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List;
@Setter @Setter
@Getter @Getter
public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> { public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> implements
ZigConfigEditor.FilePathModule.Carrier, ZigConfigEditor.ColoredModule.Carrier {
public String filePath = ""; public String filePath = "";
public boolean colored = true;
public ZigExecConfigRun(@NotNull Project project, @NotNull ConfigurationFactory factory) { public ZigExecConfigRun(@NotNull Project project, @NotNull ConfigurationFactory factory) {
super(project, factory, "Zig Run"); super(project, factory, "Zig Run");
} }
@Override @Override
public String[] buildCommandLineArgs() { public String[] buildCommandLineArgs() {
return new String[]{"run", filePath}; return new String[]{"run", "--color", colored ? "on" : "off", filePath};
} }
@Override @Override
@ -49,8 +54,11 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
} }
@Override @Override
public @NotNull Editor getConfigurationEditor() { public @NotNull List<ZigConfigEditor.ZigConfigModule<ZigExecConfigRun>> getEditorConfigModules() {
return new Editor(); val modules = super.getEditorConfigModules();
modules.add(new ZigConfigEditor.FilePathModule<>());
modules.add(new ZigConfigEditor.ColoredModule<>());
return modules;
} }
@Override @Override
@ -62,10 +70,8 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
public void readExternal(@NotNull Element element) throws InvalidDataException { public void readExternal(@NotNull Element element) throws InvalidDataException {
super.readExternal(element); super.readExternal(element);
var filePath = ElementUtil.readString(element, "filePath"); ElementUtil.readString(element, "filePath").ifPresent(x -> filePath = x);
if (filePath != null) { ElementUtil.readBoolean(element, "colored").ifPresent(x -> colored = x);
this.filePath = filePath;
}
} }
@Override @Override
@ -73,18 +79,6 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
super.writeExternal(element); super.writeExternal(element);
ElementUtil.writeString(element, "filePath", filePath); ElementUtil.writeString(element, "filePath", filePath);
} ElementUtil.writeBoolean(element, "colored", colored);
public static class Editor extends ZigConfigEditor.WithFilePath<ZigExecConfigRun> {
@Override
protected String getFilePath(ZigExecConfigRun config) {
return config.filePath;
}
@Override
protected void setFilePath(ZigExecConfigRun config, String path) {
config.filePath = path;
}
} }
} }

View file

@ -19,24 +19,36 @@ package com.falsepattern.zigbrains.project.execution.test;
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase; import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase; import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
import com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor; import com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor;
import com.falsepattern.zigbrains.project.util.ElementUtil;
import com.intellij.execution.Executor; import com.intellij.execution.Executor;
import com.intellij.execution.configurations.ConfigurationFactory; import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> { import java.util.List;
@Getter
@Setter
public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> implements ZigConfigEditor.FilePathModule.Carrier,
ZigConfigEditor.ColoredModule.Carrier {
public String filePath = ""; public String filePath = "";
public boolean colored = true;
public ZigExecConfigTest(@NotNull Project project, @NotNull ConfigurationFactory factory) { public ZigExecConfigTest(@NotNull Project project, @NotNull ConfigurationFactory factory) {
super(project, factory, "Zig Test"); super(project, factory, "Zig Test");
} }
@Override @Override
public String[] buildCommandLineArgs() { public String[] buildCommandLineArgs() {
return new String[]{"test", filePath}; return new String[]{"test", "--color", colored ? "on" : "off", filePath};
} }
@Override @Override
@ -50,20 +62,26 @@ public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> {
} }
@Override @Override
public @NotNull SettingsEditor<? extends RunConfiguration> getConfigurationEditor() { public void readExternal(@NotNull Element element) throws InvalidDataException {
return new Editor(); super.readExternal(element);
}
public static class Editor extends ZigConfigEditor.WithFilePath<ZigExecConfigTest> { ElementUtil.readString(element, "filePath").ifPresent(x -> filePath = x);
ElementUtil.readBoolean(element, "colored").ifPresent(x -> colored = x);
@Override
protected String getFilePath(ZigExecConfigTest config) {
return config.filePath;
} }
@Override @Override
protected void setFilePath(ZigExecConfigTest config, String path) { public void writeExternal(@NotNull Element element) {
config.filePath = path; super.writeExternal(element);
ElementUtil.writeString(element, "filePath", filePath);
ElementUtil.writeBoolean(element, "colored", colored);
} }
@Override
public @NotNull List<ZigConfigEditor.ZigConfigModule<ZigExecConfigTest>> getEditorConfigModules() {
val modules = super.getEditorConfigModules();
modules.add(new ZigConfigEditor.FilePathModule<>());
modules.add(new ZigConfigEditor.ColoredModule<>());
return modules;
} }
} }

View file

@ -16,16 +16,22 @@
package com.falsepattern.zigbrains.project.runconfig; package com.falsepattern.zigbrains.project.runconfig;
import com.falsepattern.zigbrains.common.util.StringUtil;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.PtyCommandLine; import com.intellij.execution.configurations.PtyCommandLine;
import com.intellij.execution.process.AnsiEscapeDecoder;
import com.intellij.execution.process.KillableColoredProcessHandler;
import com.intellij.execution.process.KillableProcessHandler; import com.intellij.execution.process.KillableProcessHandler;
import com.intellij.openapi.util.Key;
import com.pty4j.PtyProcess; import com.pty4j.PtyProcess;
import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays;
public class ZigProcessHandler extends KillableProcessHandler { public class ZigProcessHandler extends KillableColoredProcessHandler implements AnsiEscapeDecoder.ColoredTextAcceptor {
public ZigProcessHandler(@NotNull GeneralCommandLine commandLine) throws ExecutionException { public ZigProcessHandler(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
super(commandLine); super(commandLine);
setHasPty(commandLine instanceof PtyCommandLine); setHasPty(commandLine instanceof PtyCommandLine);
@ -37,4 +43,9 @@ public class ZigProcessHandler extends KillableProcessHandler {
setHasPty(process instanceof PtyProcess); setHasPty(process instanceof PtyProcess);
setShouldDestroyProcessRecursively(!hasPty()); setShouldDestroyProcessRecursively(!hasPty());
} }
@Override
public void coloredTextAvailable(@NotNull String text, @NotNull Key attributes) {
super.coloredTextAvailable(StringUtil.translateVT100Escapes(text), attributes);
}
} }

View file

@ -20,8 +20,10 @@ import lombok.val;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class ElementUtil { public class ElementUtil {
public static @Nullable String readString(Element element, String name) { public static Optional<String> readString(Element element, String name) {
return element.getChildren() return element.getChildren()
.stream() .stream()
.filter(it -> it.getName() .filter(it -> it.getName()
@ -29,8 +31,7 @@ public class ElementUtil {
it.getAttributeValue("name") it.getAttributeValue("name")
.equals(name)) .equals(name))
.findAny() .findAny()
.map(it -> it.getAttributeValue("value")) .map(it -> it.getAttributeValue("value"));
.orElse(null);
} }
public static void writeString(Element element, String name, String value) { public static void writeString(Element element, String name, String value) {
@ -40,4 +41,12 @@ public class ElementUtil {
element.addContent(option); element.addContent(option);
} }
public static void writeBoolean(Element element, String name, boolean state) {
writeString(element, name, Boolean.toString(state));
}
public static Optional<Boolean> readBoolean(Element element, String name) {
return readString(element, name).map(Boolean::parseBoolean);
}
} }

View file

@ -108,6 +108,9 @@
bundle="zigbrains.zig.Bundle" bundle="zigbrains.zig.Bundle"
key="notif-zls" key="notif-zls"
id="ZigBrains.ZLS"/> id="ZigBrains.ZLS"/>
<consoleFilterProvider implementation="com.falsepattern.zigbrains.project.console.ZigConsoleFilterProvider"/>
<analyzeStacktraceFilter implementation="com.falsepattern.zigbrains.project.console.ZigSourceFileFilter"/>
</extensions> </extensions>