diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/AbstractDebugAdapterLaunchShortcut.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/AbstractDebugAdapterLaunchShortcut.java index 54c5abc4c9..2f13433e19 100644 --- a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/AbstractDebugAdapterLaunchShortcut.java +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/AbstractDebugAdapterLaunchShortcut.java @@ -6,7 +6,7 @@ * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 - * + * * Contributors: * Pierre-Yves B. - Issue #309 Launch called from the wrong thread *******************************************************************************/ @@ -43,6 +43,7 @@ import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.part.FileEditorInput; +import org.eclipse.wildwebdeveloper.util.FileUtils; public abstract class AbstractDebugAdapterLaunchShortcut implements ILaunchShortcut2 { @@ -156,7 +157,7 @@ private void launch(String mode, ILaunchConfiguration[] configurations, File lau DebugUITools.launch(configuration, mode); } else { if (DebugUIPlugin.openLaunchConfigurationEditDialog(Display.getCurrent().getActiveShell(), configuration, DebugUITools.getLaunchGroup(configuration, mode).getIdentifier(), null, true) == IDialogConstants.OK_ID) { - DebugUITools.launch(configuration, mode); + DebugUITools.launch(configuration, mode); } } }); @@ -202,7 +203,7 @@ private ILaunchConfigurationWorkingCopy createNewLaunchConfiguration(File file) String configName = launchManager.generateLaunchConfigurationName(file.getAbsolutePath()); ILaunchConfigurationWorkingCopy wc = configType.newInstance(null, configName); wc.setAttribute(DebugPlugin.ATTR_WORKING_DIRECTORY, file.getParentFile().getAbsolutePath()); - wc.setMappedResources(ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(file.toURI())); + wc.setMappedResources(ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(FileUtils.toUri(file))); configureLaunchConfiguration(file, wc); return wc; } @@ -210,7 +211,7 @@ private ILaunchConfigurationWorkingCopy createNewLaunchConfiguration(File file) /** * Takes a working copy of a launch configuration and sets the default * attributes according to provided file - * + * * @param file * @param wc */ diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/AbstractRunHTMLDebugTab.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/AbstractRunHTMLDebugTab.java index 6898cfe9c4..6434ea2d78 100644 --- a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/AbstractRunHTMLDebugTab.java +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/AbstractRunHTMLDebugTab.java @@ -56,9 +56,10 @@ import org.eclipse.wildwebdeveloper.Activator; import org.eclipse.wildwebdeveloper.debug.chrome.ChromeRunDAPDebugDelegate; import org.eclipse.wildwebdeveloper.debug.chrome.ChromeRunDebugLaunchShortcut; +import org.eclipse.wildwebdeveloper.util.FileUtils; public abstract class AbstractRunHTMLDebugTab extends AbstractLaunchConfigurationTab { - + private Text programPathText; private Text argumentsText; private Text workingDirectoryText; @@ -82,8 +83,8 @@ public AbstractRunHTMLDebugTab() { public void createControl(Composite parent) { resComposite = new Composite(parent, SWT.NONE); resComposite.setLayout(new GridLayout(4, false)); - - fileRadio = createRadioButton(resComposite, Messages.FirefoxDebugTab_File); + + fileRadio = createRadioButton(resComposite, Messages.FirefoxDebugTab_File); fileRadio.setToolTipText(Messages.AbstractRunHTMLDebugTab_fileRadioToolTip); fileRadio.setLayoutData(new GridData(SWT.DEFAULT, SWT.DEFAULT, false, false)); fileRadio.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { @@ -96,7 +97,7 @@ public void createControl(Composite parent) { validateProgramPathAndURL(); updateLaunchConfigurationDialog(); })); - + this.programPathText = new Text(resComposite, SWT.BORDER); this.programPathText.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1)); fileDecoration = new ControlDecoration(programPathText, SWT.TOP | SWT.LEFT); @@ -118,7 +119,7 @@ public void createControl(Composite parent) { programPathText.setText(path); } })); - + urlRadio = createRadioButton(resComposite, "URL: "); urlRadio.setToolTipText(Messages.RunFirefoxDebugTab_URL_Note); urlRadio.setLayoutData(new GridData(SWT.DEFAULT, SWT.DEFAULT, false, false)); @@ -142,7 +143,7 @@ public void createControl(Composite parent) { validateProgramPathAndURL(); updateLaunchConfigurationDialog(); }); - + new Label(resComposite, SWT.NONE).setText(Messages.AbstractRunHTMLDebugTab_webRoot_folder); webRootText = new Text(resComposite, SWT.BORDER); webRootText.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false)); @@ -232,7 +233,7 @@ public void createControl(Composite parent) { private void validateProgramPathAndURL() { setDirty(true); - + setErrorMessage(null); fileDecoration.hide(); urlDecoration.hide(); @@ -252,23 +253,23 @@ private void validateProgramPathAndURL() { } catch (CoreException ex) { errorMessage = ex.getMessage(); } - + if (errorMessage != null) { setErrorMessage(errorMessage); fileDecoration.setDescriptionText(errorMessage); fileDecoration.show(); } - + } else if (urlRadio.getSelection()) { try { new URL(urlText.getText()); } catch (MalformedURLException ex) { errorMessage = MessageFormat.format( - Messages.RunProgramTab_error_malformedUR, + Messages.RunProgramTab_error_malformedUR, ex.getMessage()); urlDecoration.setDescriptionText(errorMessage); urlDecoration.show(); - } + } boolean showWebRootDecoration = false; if(webRootText.getText().isBlank()) { errorMessage = Messages.AbstractRunHTMLDebugTab_cannot_debug_without_webroot; @@ -338,9 +339,9 @@ public void initializeFrom(ILaunchConfiguration configuration) { webRootProjectSelectButton.setEnabled(true); webRootFilesystemSelectButton.setEnabled(true); } - + validateProgramPathAndURL(); - + } catch (CoreException e) { ILog.get().log(e.getStatus()); } @@ -357,7 +358,7 @@ public void performApply(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute(AbstractHTMLDebugDelegate.ARGUMENTS, this.argumentsText.getText()); String workingDirectory = this.workingDirectoryText.getText(); configuration.setAttribute(DebugPlugin.ATTR_WORKING_DIRECTORY, workingDirectory); - configuration.setMappedResources(ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(new File(programPath).toURI())); + configuration.setMappedResources(ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(FileUtils.toUri(programPath))); } @Override diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/node/RunProgramTab.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/node/RunProgramTab.java index 3f77a0edf8..9974bf2cca 100644 --- a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/node/RunProgramTab.java +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/node/RunProgramTab.java @@ -37,6 +37,7 @@ import org.eclipse.swt.widgets.Text; import org.eclipse.wildwebdeveloper.debug.LaunchConstants; import org.eclipse.wildwebdeveloper.debug.Messages; +import org.eclipse.wildwebdeveloper.util.FileUtils; public class RunProgramTab extends AbstractLaunchConfigurationTab { @@ -127,7 +128,7 @@ public void performApply(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute(LaunchConstants.PROGRAM, programPath); configuration.setAttribute(NodeRunDAPDebugDelegate.ARGUMENTS, this.argumentsText.getText()); configuration.setAttribute(DebugPlugin.ATTR_WORKING_DIRECTORY, this.workingDirectoryText.getText()); - configuration.setMappedResources(ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(new File(programPath).toURI())); + configuration.setMappedResources(ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(FileUtils.toUri(programPath))); } @Override diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/npm/NpmLaunchTab.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/npm/NpmLaunchTab.java index 933e0ceb0b..a9c53344db 100644 --- a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/npm/NpmLaunchTab.java +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/debug/npm/NpmLaunchTab.java @@ -39,6 +39,7 @@ import org.eclipse.wildwebdeveloper.debug.AbstractDebugAdapterLaunchShortcut; import org.eclipse.wildwebdeveloper.debug.AbstractHTMLDebugDelegate; import org.eclipse.wildwebdeveloper.debug.LaunchConstants; +import org.eclipse.wildwebdeveloper.util.FileUtils; public class NpmLaunchTab extends AbstractLaunchConfigurationTab { @@ -168,7 +169,7 @@ public void performApply(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute(LaunchConstants.PROGRAM, programPath); configuration.setAttribute(AbstractHTMLDebugDelegate.ARGUMENTS, this.argumentsCombo.getText()); configuration.setAttribute(DebugPlugin.ATTR_WORKING_DIRECTORY, workingDirectory); - configuration.setMappedResources(ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(new File(programPath).toURI())); + configuration.setMappedResources(ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(FileUtils.toUri(programPath))); } @Override diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/eslint/ESLintClientImpl.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/eslint/ESLintClientImpl.java index b85d734fe4..ef049c2950 100644 --- a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/eslint/ESLintClientImpl.java +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/eslint/ESLintClientImpl.java @@ -6,15 +6,13 @@ * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 - * + * * Contributors: * Pierre-Yves Bigourdan - Allow configuring directory of ESLint package *******************************************************************************/ package org.eclipse.wildwebdeveloper.eslint; import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; import java.util.HashMap; @@ -32,6 +30,7 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.browser.IWorkbenchBrowserSupport; import org.eclipse.wildwebdeveloper.jsts.ui.preferences.JSTSPreferenceServerConstants; +import org.eclipse.wildwebdeveloper.util.FileUtils; public class ESLintClientImpl extends LanguageClientImpl implements ESLintLanguageServerExtension { @@ -43,7 +42,7 @@ public CompletableFuture confirmESLintExecution(Object param) { @Override public CompletableFuture> configuration(ConfigurationParams configurationParams) { ConfigurationItem configurationItem = configurationParams.getItems().get(0); - + Map config = new HashMap<>(6, 1.f); // search first for the highest directory that has a package.json file @@ -52,29 +51,24 @@ public CompletableFuture> configuration(ConfigurationParams configu // until the workspaceFolder which folder is best suited for its workingDirectoy (where the config files are in) // also this workspaceFolder is also used to find the node models (eslint module) // because we set the nodePath below to this same directory. - File highestPackageJsonDir = null; - try { - highestPackageJsonDir = new File(new URI(configurationItem.getScopeUri())).getParentFile(); - File parentFile = highestPackageJsonDir; - while (parentFile != null) { - if (new File(parentFile, "package.json").exists()) highestPackageJsonDir = parentFile; - parentFile = parentFile.getParentFile(); - } - } catch (URISyntaxException e) { - // shouldn't happen else what to do here? + File highestPackageJsonDir = FileUtils.fromUri(configurationItem.getScopeUri()).getParentFile(); + File parentFile = highestPackageJsonDir; + while (parentFile != null) { + if (new File(parentFile, "package.json").exists()) highestPackageJsonDir = parentFile; + parentFile = parentFile.getParentFile(); } - - // `pre-release/2.3.0`: Disable using experimental Flat Config system + + // `pre-release/2.3.0`: Disable using experimental Flat Config system config.put("experimental", Collections.emptyMap()); // `pre-release/2.3.0`: Add stub `problems` settings due to: // ESLint: Cannot read properties of undefined (reading \u0027shortenToSingleLine\u0027). Please see the \u0027ESLint\u0027 output channel for details. config.put("problems", Collections.emptyMap()); - config.put("workspaceFolder", Collections.singletonMap("uri", highestPackageJsonDir.toURI().toString())); + config.put("workspaceFolder", Collections.singletonMap("uri", FileUtils.toUri(highestPackageJsonDir).toString())); // if you set a workspaceFolder and then the working dir in auto mode eslint will try to get to the right config location. - config.put("workingDirectory", Collections.singletonMap("mode", "auto")); + config.put("workingDirectory", Collections.singletonMap("mode", "auto")); // this should not point to a nodejs executable but to a parent directory containing the ESLint package config.put("nodePath",getESLintPackageDir(highestPackageJsonDir)); @@ -83,7 +77,7 @@ public CompletableFuture> configuration(ConfigurationParams configu config.put("run", "onType"); config.put("rulesCustomizations", Collections.emptyList()); - config.put("codeAction", Map.of("disableRuleComment", Map.of("enable", "true", "location", "separateLine"), + config.put("codeAction", Map.of("disableRuleComment", Map.of("enable", "true", "location", "separateLine"), "showDocumentation", Collections.singletonMap("enable", "true"))); return CompletableFuture.completedFuture(Collections.singletonList(config)); } @@ -112,7 +106,7 @@ public CompletableFuture eslintStatus(Object o) { // ignore for now return CompletableFuture.completedFuture(null); } - + @Override public CompletableFuture openDoc(Map data) { if (data.containsKey("url")) { @@ -127,7 +121,7 @@ public CompletableFuture openDoc(Map data) { } return CompletableFuture.completedFuture(null); } - + @Override public CompletableFuture noLibrary(Map> data) { MessageParams params = new MessageParams(MessageType.Info, "No ES Library found for file: " + data.get("source").get("uri")); @@ -137,7 +131,7 @@ public CompletableFuture noLibrary(Map> data) { @Override public CompletableFuture noConfig(Map> data) { - MessageParams params = new MessageParams(MessageType.Info, "No ES Configuration found for file: " + data.get("source").get("uri") + MessageParams params = new MessageParams(MessageType.Info, "No ES Configuration found for file: " + data.get("source").get("uri") + ": " + data.get("message")); logMessage(params); return CompletableFuture.completedFuture(null); diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/util/FileUtils.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/util/FileUtils.java new file mode 100644 index 0000000000..24b0c5ec9d --- /dev/null +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/util/FileUtils.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2025 Vegard IT GmbH and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Sebastian Thomschke (Vegard IT GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.wildwebdeveloper.util; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Paths; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.wildwebdeveloper.Activator; + +public class FileUtils { + private static final String FILE_SCHEME = "file"; //$NON-NLS-1$ + + public static File fromUri(String uri) { + // not using `new File(new URI(uri))` here which does not support Windows UNC paths + // and instead throws IllegalArgumentException("URI has an authority component") + return Paths.get(URI.create(uri)).toFile(); + } + + public static URI toUri(String filePath) { + return toUri(new File(filePath)); + } + + public static URI toUri(File file) { + // copied from org.eclipse.lsp4e.LSPEclipseUtils#toUri(File) + + // URI scheme specified by language server protocol and LSP + try { + final var path = file.getAbsoluteFile().toURI().getPath(); + if (path.startsWith("//")) { // UNC path like //localhost/c$/Windows/ //$NON-NLS-1$ + // split: authority = "localhost", absPath = "/c$/Windows/" + final int slash = path.indexOf('/', 2); + final String authority = slash > 2 ? path.substring(2, slash) : path.substring(2); + final String absPath = slash > 2 ? path.substring(slash) : "/"; //$NON-NLS-1$ + return new URI(FILE_SCHEME, authority, absPath, null); + } + return new URI(FILE_SCHEME, "", path, null); //$NON-NLS-1$ + } catch (URISyntaxException ex) { + Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, ex.getMessage(), ex)); + return file.getAbsoluteFile().toURI(); + } + } + +}