8000 CodeEditor suggestion by KremnevDmitry · Pull Request #4262 · jmix-framework/jmix · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

CodeEditor suggestion #4262

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package io.jmix.flowui.kit.component.codeeditor;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vaadin.flow.component.*;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
Expand All @@ -24,7 +27,18 @@
import com.vaadin.flow.component.shared.HasValidationProperties;
import com.vaadin.flow.data.binder.*;
import com.vaadin.flow.shared.Registration;
import elemental.json.JsonFactory;
import elemental.json.JsonValue;
import elemental.json.impl.JreJsonFactory;
import io.jmix.flowui.kit.component.HasTitle;
import io.jmix.flowui.kit.component.codeeditor.autocomplete.Suggester;
import io.jmix.flowui.kit.component.codeeditor.autocomplete.Suggestion;
import jakarta.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.List;

/**
* Code Editor is a multi-line text area to display and enter source code featured
Expand All @@ -33,14 +47,16 @@
@Tag("jmix-code-editor")
@NpmPackage(
value = "ace-builds",
version = "1.18.0"
version = "1.39.0"
)
@JsModule("./src/code-editor/jmix-code-editor.js")
public class JmixCodeEditor extends AbstractSinglePropertyField<JmixCodeEditor, String>
implements CompositionNotifier, Focusable<JmixCodeEditor>, HasClientValidation, HasHelper,
HasLabel, HasTitle, HasSize, HasStyle, HasTooltip, HasValidationProperties,
HasValidator<String>, InputNotifier, KeyNotifier {

private static final Logger log = LoggerFactory.getLogger(JmixCodeEditor.class);

protected static final String CODE_EDITOR_VALUE_CHANGED_EVENT = "value-changed";
protected static final String PROPERTY_THEME_CHANGED_EVENT = "theme-changed";

Expand All @@ -61,6 +77,10 @@ public class JmixCodeEditor extends AbstractSinglePropertyField<JmixCodeEditor,
protected static final String FONT_SIZE_DEFAULT_VALUE = "1rem";

protected CodeEditorValidationSupport validationSupport;
protected Suggester suggester;

protected ObjectMapper objectMapper;
protected JsonFactory jsonFactory;

public JmixCodeEditor() {
super(PROPERTY_VALUE, "", true);
Expand Down Expand Up @@ -307,6 +327,97 @@ public void setRequired(boolean required) {
getValidationSupport().setRequired(required);
}

/**
* @return {@code true} if default suggestions based on the current editor mode are enabled, {@code false} otherwise
* @see #setMode(CodeEditorMode)
*/
public boolean isDefaultSuggestionsEnabled() {
return getElement().getProperty("defaultSuggestionsEnabled", false);
}

/**
* Sets whether default suggestions based on the current editor mode are enabled.
*
* @param defaultSuggestionsEnabled whether to enable default suggestions
*/
public void setDefaultSuggestionsEnabled(boolean defaultSuggestionsEnabled) {
getElement().setProperty("defaultSuggestionsEnabled", defaultSuggestionsEnabled);
}

/**
* @return {@code true} if live suggestions that are suggested while typing are enabled, {@code false} otherwise
*/
public boolean isLiveSuggestionsEnabled() {
return getElement().getProperty("liveSuggestionsEnabled", false);
}

/**
* Sets whether live suggestions that are suggested while typing are enabled.
*
* @param liveSuggestionsEnabled whether to enable live suggestions
*/
public void setLiveSuggestionsEnabled(boolean liveSuggestionsEnabled) {
getElement().setProperty("liveSuggestionsEnabled", liveSuggestionsEnabled);
}

/**
* @return regular expression that triggers the display of a popup with suggestions
*/
@Nullable
public String getSuggestOn() {
return getElement().getProperty("suggestOn");
}

/**
* Sets the regular expression that triggers the display of a popup with suggestions.
* If the end-user enters a sequence of characters that matches the regular expression then
* a popup with suggestions will open.
*
* @param suggestOn regular expression that triggers the display of a popup with suggestions
*/
public void setSuggestOn(String suggestOn) {
getElement().setProperty("suggestOn", suggestOn);
}

/**
* @return the current editor suggester that provides suggestion options on request from the client-side
*/
@Nullable
public Suggester getSuggester() {
return suggester;
}

/**
* Sets the {@link Suggester} that provides {@link Suggestion suggestion options} on request from client-side.
*
* @param suggester suggester to set
* @see #setSuggestOn(String)
*/
public void setSuggester(@Nullable Suggester suggester) {
this.suggester = suggester;

getElement().setProperty("serverSuggesterSet", suggester != null);
}

/**
* Returns a serialized list of suggestions for the client-side. The list is generated using a {@link #suggester}
* based on the current state of the editor on the client-side.
*
* @param value current value from the client-side of the editor (may not match the server-side value)
* @param cursorPosition current cursor position in the editor
* @param prefix literal before current cursor position
* @return serialized list of suggestions
* @see #setSuggester(Suggester)
*/
@ClientCallable
protected JsonValue getSuggestions(String value, Double cursorPosition, String prefix) {
List<Suggestion> suggestions = suggester == null
? Collections.emptyList()
: suggester.getSuggestions(new Suggester.SuggestionContext(value, cursorPosition.intValue(), prefix));

return serialize(suggestions);
}

@Override
protected void setPresentationValue(String newPresentationValue) {
getElement().setProperty(PROPERTY_VALUE, newPresentationValue);
Expand Down Expand Up @@ -357,6 +468,37 @@ protected CodeEditorValidationSupport getValidationSupport() {
return validationSupport;
}

protected JsonValue serialize(Object object) {
String rawJson;

try {
rawJson = getObjectMapper().writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new IllegalStateException("Cannot serialize", e);
}

log.debug("Serialized {}", rawJson);

return getJsonFactory().parse(rawJson);
}

protected ObjectMapper getObjectMapper() {
if (objectMapper == null) {
objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

return objectMapper;
}

protected JsonFactory getJsonFactory() {
if (jsonFactory == null) {
jsonFactory = new JreJsonFactory();
}

return jsonFactory;
}

/**
* An event fired when the value of a {@link JmixCodeEditor} changes.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2025 Haulmont.
*
* 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 io.jmix.flowui.kit.component.codeeditor.autocomplete;

import io.jmix.flowui.kit.component.codeeditor.JmixCodeEditor;

import java.util.List;

/**
* Functional interface for creating code completion suggestions for the {@link JmixCodeEditor} component.
* Autocompletions are suggested based on the current state of the client-side of the component
* described by {@link SuggestionContext}.
*/
@FunctionalInterface
public interface Suggester {

/**
* @param context the current state of the client-side component
* @return life of suggestions for the current state of the component
* @see Suggestion
*/
List<Suggestion> getSuggestions(SuggestionContext context);

/**
* A class that describes the current state of the client-side of the {@link JmixCodeEditor} component used
* to prepare suggestions.
*/
class SuggestionContext {

protected String text;
protected int cursorPosition;
protected String prefix;

/**
* @param text current value from the client-side of the editor (may not match the server-side value)
* @param cursorPosition current cursor position in the editor
* @param prefix literal before current cursor position
*/
public SuggestionContext(String text, int cursorPosition, String prefix) {
this.text = text;
this.cursorPosition = cursorPosition;
this.prefix = prefix;
}

/**
* @return current value from client-side of the editor
*/
public String getText() {
return text;
}

/**
* @return current cursor position in the editor
*/
public int getCursorPosition() {
return cursorPosition;
}

/**
* @return literal before current cursor position
*/
public String getPrefix() {
return prefix;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2025 Haulmont.
*
* 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 io.jmix.flowui.kit.component.codeeditor.autocomplete;

import io.jmix.flowui.kit.component.codeeditor.JmixCodeEditor; F438
import jakarta.annotation.Nullable;

import java.io.Serializable;

/**
* Class for single suggestion from {@link Suggester} for {@link JmixCodeEditor} component.
*/
public class Suggestion implements Serializable {

protected String displayText;
protected String suggestionText;
protected String descriptionText;

/**
* @param displayText text displayed in the popup window for suggestions
* @param suggestionText the value inserted into the editor if the suggestion is selected
*/
public Suggestion(String displayText, String suggestionText) {
this(displayText, suggestionText, null);
}

/**
* @param displayText text displayed in the popup window for suggestions
* @param suggestionText the value inserted into the editor if the suggestion is selected
* @param descriptionText the hint text that appears next to the {@link #displayText} in the popup window
* for suggestions
*/
public Suggestion(String displayText, String suggestionText, @Nullable String descriptionText) {
this.displayText = displayText;
this.suggestionText = suggestionText;
this.descriptionText = descriptionText;
}

/**
* @return text displayed in the popup window for suggestions
*/
public String getDisplayText() {
return displayText;
}

/**
* @return the value inserted into the editor if the suggestion is selected
*/
public String getSuggestionText() {
return suggestionText;
}

/**
* @return the hint text that appears next to the {@link #displayText} in the popup window
* for suggestions
*/
public String getDescriptionText() {
return descriptionText;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3013,6 +3013,7 @@ public interface StudioComponents {
@StudioProperty(xmlAttribute = "css", category = StudioProperty.Category.LOOK_AND_FEEL, type = StudioPropertyType.STRING),
@StudioProperty(xmlAttribute = "colspan", category = StudioProperty.Category.POSITION, type = StudioPropertyType.INTEGER),
@StudioProperty(xmlAttribute = "dataContainer", category = StudioProperty.Category.DATA_BINDING, type = StudioPropertyType.COLLECTION_OR_INSTANCE_DATA_CONTAINER_REF),
@StudioProperty(xmlAttribute = "defaultSuggestionsEnabled", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.BOOLEAN),
@StudioProperty(xmlAttribute = "enabled", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.BOOLEAN,
defaultValue = "true"),
@StudioProperty(xmlAttribute = "errorMessage", category = StudioProperty.Category.VALIDATION, type = StudioPropertyType.LOCALIZED_STRING),
Expand All @@ -3026,6 +3027,7 @@ public interface StudioComponents {
defaultValue = "true"),
@StudioProperty(xmlAttribute = "id", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.COMPONENT_ID),
@StudioProperty(xmlAttribute = "label", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.LOCALIZED_STRING),
@StudioProperty(xmlAttribute = "liveSuggestionsEnabled", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.BOOLEAN),
@StudioProperty(xmlAttribute = "maxHeight", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}),
@StudioProperty(xmlAttribute = "maxWidth", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}),
@StudioProperty(xmlAttribute = "minHeight", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}),
Expand Down Expand Up @@ -3069,6 +3071,7 @@ public interface StudioComponents {
defaultValue = "true"),
@StudioProperty(xmlAttribute = "showPrintMargin", category = StudioProperty.Category.LOOK_AND_FEEL, type = StudioPropertyType.BOOLEAN,
defaultValue = "true"),
@StudioProperty(xmlAttribute = "suggestOn", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.STRING),
@StudioProperty(xmlAttribute = "tabIndex", type = StudioPropertyType.INTEGER),
@StudioProperty(xmlAttribute = "textWrap", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.BOOLEAN,
defaultValue = "false"),
Expand Down
Loading
0