Skip to content

Commit

Permalink
feat: sync settings with .cdsprettier.json changes (#98)
Browse files Browse the repository at this point in the history
Also includes:
* chore: deduplicate textmate-bundle registration
* chore: avoid redundant settings reload
  • Loading branch information
tim-sh authored Jan 10, 2025
1 parent 266f727 commit a6de1e5
Show file tree
Hide file tree
Showing 21 changed files with 463 additions and 245 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.gradle
.idea
.intellijPlatform
.kotlin
.qodana

local.properties
Expand Down
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.jetbrains.intellij.platform.gradle.TestFrameworkType
plugins {
id 'java'
id 'org.jetbrains.intellij.platform' version '2.0.1'
id 'org.jetbrains.kotlin.jvm' version '2.0.21'
}

def hasLocalProperties = provider {
Expand All @@ -30,6 +31,7 @@ repositories {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'org.jetbrains:annotations:26.0.1'
implementation 'org.json:json:20240303'
implementation 'org.apache.maven:maven-artifact:3.9.9'
Expand Down Expand Up @@ -108,6 +110,11 @@ tasks {

test {
dependsOn 'copyLspToInstrumented'
exclude '**/*TestBase.class'
}

jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

publishPlugin {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public void apply(@NotNull CodeStyleSettings settings) {
super.apply(settings); // Applies settings from UI to the settings object
CdsCodeStyleSettings cdsSettings = settings.getCustomSettings(CdsCodeStyleSettings.class);
setOptionsEnablement(cdsSettings.getChildOptionsEnablement(category));
CdsPreviewFormattingService.acceptSettings(cdsSettings);
CdsCodeStylePreviewFormattingService.acceptSettings(cdsSettings);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,15 @@
import com.sap.cap.cds.intellij.lang.CdsLanguage;
import org.jetbrains.annotations.NotNull;

import static com.intellij.openapi.application.ApplicationManager.getApplication;

public class CdsCodeStyleMainPanel extends TabbedLanguageCodeStylePanel {

protected CdsCodeStyleMainPanel(CodeStyleSettings currentSettings, @NotNull CodeStyleSettings settings) {
super(CdsLanguage.INSTANCE, currentSettings, settings);

// Enable saving of code-style settings
for (Project project : ProjectManager.getInstance().getOpenProjects()) {
project.getService(CdsProjectCodeStyleSettingsService.class);
project.getService(CdsCodeStyleSettingsService.class);
}
getApplication().getService(CdsDefaultCodeStyleSettingsService.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,23 @@
import java.util.Set;

import static com.sap.cap.cds.intellij.codestyle.CdsCodeStyleSettingsProvider.SAMPLE_FILE_NAME;
import static com.sap.cap.cds.intellij.codestyle.CdsPrettierJsonService.PRETTIER_JSON;
import static com.sap.cap.cds.intellij.codestyle.CdsCodeStyleSettingsService.PRETTIER_JSON;
import static com.sap.cap.cds.intellij.lsp.CdsLspServerDescriptor.getFormattingCommandLine;
import static com.sap.cap.cds.intellij.util.FileUtil.createTempDir;
import static java.nio.file.Files.readString;
import static java.nio.file.Files.write;

public class CdsPreviewFormattingService implements FormattingService {
public class CdsCodeStylePreviewFormattingService implements FormattingService {

private static final Map<String, String> formattedByPrettierJson = new HashMap<>();
private static Path prettierJsonPath;
private static Path tempDir;
private static Path samplePath;
private static String prettierJson = "{}";

CdsPreviewFormattingService() {
CdsCodeStylePreviewFormattingService() {
try {
tempDir = createTempDir(CdsPreviewFormattingService.class.getName() + "_");
tempDir = createTempDir(CdsCodeStylePreviewFormattingService.class.getName() + "_");
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package com.sap.cap.cds.intellij.codestyle;

import com.intellij.application.options.CodeStyle;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.codeStyle.CodeStyleSettingsChangeEvent;
import com.intellij.psi.codeStyle.CodeStyleSettingsListener;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import org.jetbrains.annotations.NotNull;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import static com.intellij.openapi.project.ProjectUtil.guessProjectDir;
import static com.sap.cap.cds.intellij.util.Logger.logger;
import static java.nio.file.Files.readString;

@Service(Service.Level.PROJECT)
public final class CdsCodeStyleSettingsService {

public static final String PRETTIER_JSON = ".cdsprettier.json";
private final Project project;
private final com.intellij.openapi.diagnostic.Logger logger;
private final CdsPrettierJsonManager prettierJsonManager;

public CdsCodeStyleSettingsService(Project project) {
this.project = project;
this.logger = logger(project).CODE_STYLE();
prettierJsonManager = new CdsPrettierJsonManager();
CodeStyleSettingsManager.getInstance(project).subscribe(new CodeStyleSettingsListener() {
@Override
public void codeStyleSettingsChanged(@NotNull CodeStyleSettingsChangeEvent event) {
updateSettingsFile();
}
});
}

public boolean isSettingsFilePresent() {
return prettierJsonManager.isJsonFilePresent();
}

public boolean isSettingsReallyChanged() {
return prettierJsonManager.isSettingsReallyChanged();
}

public void updateSettingsFile() {
prettierJsonManager.saveSettingsToFile(getSettings());
}

public void updateProjectSettingsFromFile() {
if (CodeStyle.usesOwnSettings(project)) {
if (isSettingsFilePresent()) {
prettierJsonManager.loadSettingsFromFile(getSettings());
} else {
prettierJsonManager.reset();
CodeStyle.setMainProjectSettings(project, CodeStyleSettingsManager.getInstance(project).createSettings());
}
}
}

private CdsCodeStyleSettings getSettings() {
return CodeStyle.getSettings(project).getCustomSettings(CdsCodeStyleSettings.class);
}

private final class CdsPrettierJsonManager {

static final int JSON_INDENT = 2;

File jsonFile;
String jsonWritten;

CdsPrettierJsonManager() {
// assuming no changes to project directory
VirtualFile guessed = guessProjectDir(project);
String projectDir = guessed != null
? guessed.getPath()
: project.getBasePath();
if (projectDir != null) {
jsonFile = getJsonFile(projectDir);
}
reset();
}

boolean isJsonFilePresent() {
return jsonFile != null && jsonFile.exists();
}

void loadSettingsFromFile(@NotNull CdsCodeStyleSettings settings) {
String json = readJson();
if (json != null) {
try {
settings.loadFrom(new JSONObject(json));
} catch (JSONException e) {
logger.error("Failed to parse JSON '%s'".formatted(json), e);
}
}
}

private String readJson() {
if (!isJsonFilePresent()) {
return null;
}
try {
return readString(jsonFile.toPath());
} catch (IOException e) {
logger.error("Failed to read [%s]".formatted(jsonFile), e);
}
return null;
}

void saveSettingsToFile(@NotNull CdsCodeStyleSettings settings) {
if (jsonFile == null) {
return;
}
String json = settings.getNonDefaultSettings().toString(JSON_INDENT);
if (json.equals(jsonWritten)) {
return;
}
if (!jsonFile.getParentFile().exists()) {
logger.debug("Directory [%s] does not exist".formatted(jsonFile.getParentFile()));
return;
}
try (FileWriter writer = new FileWriter(jsonFile)) {
writer.write(json);
jsonWritten = json;
} catch (IOException e) {
logger.error("Failed to write [%s]".formatted(jsonFile), e);
}
}

void reset() {
jsonWritten = null;
}

@NotNull File getJsonFile(String projectDir) {
File file = new File(projectDir, PRETTIER_JSON);
if (!file.exists()) {
logger.debug("Optional file [%s] does not exist".formatted(file));
}
return file;
}

public boolean isSettingsReallyChanged() {
return jsonWritten == null || !jsonWritten.equals(readJson());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ public void apply(@NotNull CodeStyleSettings settings) throws ConfigurationExcep
super.apply(settings);
CdsCodeStyleSettings cdsSettings = settings.getCustomSettings(CdsCodeStyleSettings.class);
setOptionsEnablement(cdsSettings.getChildOptionsEnablement(category));
CdsPreviewFormattingService.acceptSettings(cdsSettings);
CdsCodeStylePreviewFormattingService.acceptSettings(cdsSettings);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,52 @@

import com.intellij.openapi.project.ProjectLocator;
import com.intellij.openapi.vfs.AsyncFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

import static com.intellij.openapi.application.ApplicationManager.getApplication;
import static com.sap.cap.cds.intellij.codestyle.CdsPrettierJsonService.PRETTIER_JSON;
import static com.intellij.openapi.vfs.VfsUtil.collectChildrenRecursively;
import static com.sap.cap.cds.intellij.codestyle.CdsCodeStyleSettingsService.PRETTIER_JSON;

public class CdsPrettierJsonListener implements AsyncFileListener {
@Override
public @Nullable ChangeApplier prepareChange(@NotNull List<? extends @NotNull VFileEvent> list) {
list.stream()
// NOTE this is also triggered by programmatic changes to the file
.filter(event -> event instanceof VFileContentChangeEvent && ((VFileContentChangeEvent) event).getFile().getName().equals(PRETTIER_JSON))
.map(event -> getApplication().getService(ProjectLocator.class).guessProjectForFile(event.getFile()))
private static void handle(Stream<? extends @NotNull VFileEvent> stream) {
ProjectLocator projectLocator = getApplication().getService(ProjectLocator.class);
stream
.flatMap(event -> event instanceof VFileCreateEvent e && e.getFile() != null && e.getFile().isDirectory()
? collectChildrenRecursively(e.getFile()).stream()
: Stream.of(event.getFile()))
.filter(Objects::nonNull)
.filter(file -> file.getName().equals(PRETTIER_JSON))
.map(projectLocator::guessProjectForFile)
.filter(Objects::nonNull)
.distinct()
.forEach(project -> {
getApplication().invokeLater(() -> {
project.getService(CdsProjectCodeStyleSettingsService.class).updateSettingsFromFile();
});
CdsCodeStyleSettingsService service = project.getService(CdsCodeStyleSettingsService.class);
if (service.isSettingsReallyChanged()) {
service.updateProjectSettingsFromFile();
}
});
return null;
}

@Override
public @Nullable ChangeApplier prepareChange(@NotNull List<? extends @NotNull VFileEvent> list) {
return new ChangeApplier() {
@Override
public void beforeVfsChange() {
handle(list.stream().filter(event -> event instanceof VFileDeleteEvent));
}

@Override
public void afterVfsChange() {
handle(list.stream().filter(event -> !(event instanceof VFileDeleteEvent)));
}
};
}
}
Loading

0 comments on commit a6de1e5

Please sign in to comment.