-
Notifications
You must be signed in to change notification settings - Fork 326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change the default location for Enso projects #10318
Changes from 16 commits
a69f9a2
b103c0f
91f8a56
d3c4ac5
fb896e7
42f6ba6
37f3b40
825909c
ea1dc7b
1686657
1b22f03
797b1ea
fad0ecb
6b6dcbd
be06a0e
b1a8702
23552c1
d24fb31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/** | ||
* @file This module contains the logic for the detection of user-specific desktop environment attributes. | ||
*/ | ||
|
||
import * as childProcess from 'node:child_process' | ||
import * as os from 'node:os' | ||
import * as path from 'node:path' | ||
|
||
export const DOCUMENTS = getDocumentsPath() | ||
|
||
const CHILD_PROCESS_TIMEOUT = 3000 | ||
|
||
/** | ||
* Detects path of the user documents directory depending on the operating system. | ||
*/ | ||
function getDocumentsPath(): string | undefined { | ||
if (process.platform === 'linux') { | ||
return getLinuxDocumentsPath() | ||
} else if (process.platform === 'darwin') { | ||
return getMacOsDocumentsPath() | ||
} else if (process.platform === 'win32') { | ||
return getWindowsDocumentsPath() | ||
} else { | ||
return | ||
} | ||
} | ||
|
||
/** | ||
* Returns the user documents path on Linux. | ||
*/ | ||
function getLinuxDocumentsPath(): string { | ||
const xdgDocumentsPath = getXdgDocumentsPath() | ||
|
||
return xdgDocumentsPath ?? path.join(os.homedir(), 'enso') | ||
} | ||
|
||
/** | ||
* Gets the documents directory from the XDG directory management system. | ||
*/ | ||
function getXdgDocumentsPath(): string | undefined { | ||
const out = childProcess.spawnSync('xdg-user-dir', ['DOCUMENTS'], { | ||
timeout: CHILD_PROCESS_TIMEOUT, | ||
}) | ||
|
||
if (out.error !== undefined) { | ||
return | ||
} else { | ||
return out.stdout.toString().trim() | ||
} | ||
} | ||
|
||
/** | ||
* Get the user documents path. On macOS, `Documents` acts as a symlink pointing to the | ||
* real locale-specific user documents directory. | ||
*/ | ||
function getMacOsDocumentsPath(): string { | ||
return path.join(os.homedir(), 'Documents') | ||
} | ||
|
||
/** | ||
* Get the path to the `My Documents` Windows directory. | ||
*/ | ||
function getWindowsDocumentsPath(): string | undefined { | ||
const out = childProcess.spawnSync( | ||
'reg', | ||
[ | ||
'query', | ||
'"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellFolders"', | ||
'/v', | ||
'personal', | ||
], | ||
{ timeout: CHILD_PROCESS_TIMEOUT } | ||
) | ||
|
||
if (out.error !== undefined) { | ||
return | ||
} else { | ||
const stdoutString = out.stdout.toString() | ||
return stdoutString.split('\\s\\s+')[4] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package org.enso.desktopenvironment; | ||
|
||
import org.enso.desktopenvironment.directories.Directories; | ||
import org.enso.desktopenvironment.directories.DirectoriesFactory; | ||
|
||
public final class Platform { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enum Platform {
LINUX, MAC, WINDOWS
} might be better choice from an API perspective. |
||
|
||
private static final String OS_NAME = "os.name"; | ||
private static final String LINUX = "linux"; | ||
private static final String MAC = "mac"; | ||
private static final String WINDOWS = "windows"; | ||
|
||
private Platform() {} | ||
|
||
public static Directories getDirectories() { | ||
return DirectoriesFactory.getInstance(); | ||
} | ||
|
||
public static String getOsName() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you have to define the |
||
return System.getProperty(OS_NAME); | ||
} | ||
|
||
public static boolean isLinux() { | ||
return System.getProperty(OS_NAME).toLowerCase().contains(LINUX); | ||
4e6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
public static boolean isMacOs() { | ||
return System.getProperty(OS_NAME).toLowerCase().contains(MAC); | ||
} | ||
|
||
public static boolean isWindows() { | ||
return System.getProperty(OS_NAME).toLowerCase().contains(WINDOWS); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package org.enso.desktopenvironment.directories; | ||
|
||
import java.nio.file.Path; | ||
|
||
public interface Directories { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want other code outside of this module to implement this interface? If not, then prevent it:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good old Java days 🤠 |
||
|
||
default Path getUserHome() { | ||
return Path.of(System.getProperty("user.home")); | ||
} | ||
|
||
Path getDocuments() throws DirectoriesException; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package org.enso.desktopenvironment.directories; | ||
4e6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
import java.io.IOException; | ||
|
||
/** Indicates an issue when accessing user directories. */ | ||
public final class DirectoriesException extends IOException { | ||
4e6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public DirectoriesException(String message) { | ||
super(message); | ||
} | ||
|
||
public DirectoriesException(String message, Throwable cause) { | ||
super(message, cause); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.enso.desktopenvironment.directories; | ||
|
||
import org.enso.desktopenvironment.Platform; | ||
|
||
public final class DirectoriesFactory { | ||
4e6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private static final Directories INSTANCE = initDirectories(); | ||
|
||
private static Directories initDirectories() { | ||
if (Platform.isLinux()) { | ||
return new LinuxDirectories(); | ||
} | ||
|
||
if (Platform.isMacOs()) { | ||
return new MacOsDirectories(); | ||
} | ||
|
||
if (Platform.isWindows()) { | ||
return new WindowsDirectories(); | ||
} | ||
|
||
throw new UnsupportedOperationException("Unsupported OS '" + Platform.getOsName() + "'"); | ||
} | ||
|
||
private DirectoriesFactory() {} | ||
|
||
public static Directories getInstance() { | ||
return INSTANCE; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package org.enso.desktopenvironment.directories; | ||
|
||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Path; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
class LinuxDirectories implements Directories { | ||
|
||
private static final String[] PROCESS_XDG_DOCUMENTS = new String[] {"xdg-user-dir", "DOCUMENTS"}; | ||
|
||
/** | ||
* Get the user 'Documents' directory. | ||
* | ||
* <p>Tries to obtain the documents directory from the XDG directory management system if | ||
* available and falls back to {@code $HOME/enso}. | ||
* | ||
* @return the path to the user documents directory. | ||
*/ | ||
@Override | ||
public Path getDocuments() { | ||
try { | ||
return getXdgDocuments(); | ||
} catch (IOException | InterruptedException e) { | ||
return getUserHome().resolve("enso"); | ||
} | ||
} | ||
|
||
private Path getXdgDocuments() throws IOException, InterruptedException { | ||
var process = new ProcessBuilder(PROCESS_XDG_DOCUMENTS).start(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for using |
||
process.waitFor(3, TimeUnit.SECONDS); | ||
|
||
var documentsString = | ||
new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); | ||
|
||
return Path.of(documentsString.trim()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.enso.desktopenvironment.directories; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
|
||
class MacOsDirectories implements Directories { | ||
|
||
private static final String DOCUMENTS = "Documents"; | ||
|
||
/** | ||
* Get the user documents path. | ||
* | ||
* <p>On macOS, the 'Documents' directory acts like a symlink and points to the real | ||
* locale-dependent user documents folder. | ||
* | ||
* @return the path to the user documents directory. | ||
* @throws DirectoriesException when unable to resolve the real documents path. | ||
*/ | ||
@Override | ||
public Path getDocuments() throws DirectoriesException { | ||
try { | ||
return getUserHome().resolve(DOCUMENTS).toRealPath(); | ||
} catch (IOException e) { | ||
throw new DirectoriesException("Failed to resolve real MacOs documents path", e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package org.enso.desktopenvironment.directories; | ||
|
||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Path; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
class WindowsDirectories implements Directories { | ||
|
||
private static final String[] PROCESS_REG_QUERY = | ||
new String[] { | ||
"reg", | ||
"query", | ||
"\"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellFolders\"", | ||
"/v", | ||
"personal" | ||
}; | ||
|
||
/** | ||
* Get the path to 'My Documents' user directory. | ||
* | ||
* <p>Method uses the registry query that may not work on Windows XP versions and below. | ||
* | ||
* @return the 'My Documents' user directory path. | ||
* @throws DirectoriesException when fails to detect the user documents directory. | ||
*/ | ||
@Override | ||
public Path getDocuments() throws DirectoriesException { | ||
try { | ||
var process = new ProcessBuilder(PROCESS_REG_QUERY).start(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoiding a process execution would be nicer, but not even stack over flow has a suggestion how to do that nicely. |
||
process.waitFor(3, TimeUnit.SECONDS); | ||
|
||
var stdoutString = | ||
new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); | ||
var stdoutParts = stdoutString.split("\\s\\s+"); | ||
if (stdoutParts.length < 5) { | ||
throw new DirectoriesException( | ||
"Invalid Windows registry query output: '" + stdoutString + "'"); | ||
} | ||
|
||
return Path.of(stdoutParts[4].trim()); | ||
} catch (IOException e) { | ||
throw new DirectoriesException("Failed to run Windows registry query", e); | ||
} catch (InterruptedException e) { | ||
throw new DirectoriesException("Windows registry query timeout", e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package org.enso.desktopenvironment; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
|
||
public class PlatformTest { | ||
|
||
@Test | ||
public void getDirectories() { | ||
Assert.assertNotNull(Platform.getDirectories()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No 3rd party dependencies. I like such lightweight modules.