diff --git a/bundles/com.e1c.v8codestyle.bsl.ui/META-INF/MANIFEST.MF b/bundles/com.e1c.v8codestyle.bsl.ui/META-INF/MANIFEST.MF
index 58557794..ac90b064 100644
--- a/bundles/com.e1c.v8codestyle.bsl.ui/META-INF/MANIFEST.MF
+++ b/bundles/com.e1c.v8codestyle.bsl.ui/META-INF/MANIFEST.MF
@@ -23,6 +23,7 @@ Import-Package: com._1c.g5.ides.ui.texteditor.xtext.embedded;version="[6.0.0,7.0
com._1c.g5.v8.dt.bsl.common;version="[6.0.0,7.0.0)",
com._1c.g5.v8.dt.bsl.documentation.comment;version="[4.0.0,5.0.0)",
com._1c.g5.v8.dt.bsl.model;version="[5.0.0,6.0.0)",
+ com._1c.g5.v8.dt.bsl.model.util;version="4.7.0",
com._1c.g5.v8.dt.bsl.services;version="[7.0.0,8.0.0)",
com._1c.g5.v8.dt.bsl.ui;version="[9.0.0,10.0.0)",
com._1c.g5.v8.dt.bsl.ui.contentassist;version="[8.0.0,9.0.0)",
diff --git a/bundles/com.e1c.v8codestyle.bsl.ui/plugin.xml b/bundles/com.e1c.v8codestyle.bsl.ui/plugin.xml
index 9f0c599b..a6008869 100644
--- a/bundles/com.e1c.v8codestyle.bsl.ui/plugin.xml
+++ b/bundles/com.e1c.v8codestyle.bsl.ui/plugin.xml
@@ -368,5 +368,11 @@
class="com.e1c.v8codestyle.internal.bsl.ui.ExecutableExtensionFactory:com.e1c.v8codestyle.bsl.ui.qfix.ServerExecutionSafeModeFix">
+
+
+
+
diff --git a/bundles/com.e1c.v8codestyle.bsl.ui/src/com/e1c/v8codestyle/internal/bsl/ui/services/BslModuleRegionsService.java b/bundles/com.e1c.v8codestyle.bsl.ui/src/com/e1c/v8codestyle/internal/bsl/ui/services/BslModuleRegionsService.java
new file mode 100644
index 00000000..a4fa742d
--- /dev/null
+++ b/bundles/com.e1c.v8codestyle.bsl.ui/src/com/e1c/v8codestyle/internal/bsl/ui/services/BslModuleRegionsService.java
@@ -0,0 +1,238 @@
+/**
+ * Copyright (C) 2023, 1C
+ */
+package com.e1c.v8codestyle.internal.bsl.ui.services;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.xtext.nodemodel.INode;
+import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
+import org.eclipse.xtext.ui.editor.model.IXtextDocument;
+
+import com._1c.g5.v8.dt.bsl.common.IBslModuleRegionsService;
+import com._1c.g5.v8.dt.bsl.model.RegionPreprocessor;
+import com._1c.g5.v8.dt.common.Pair;
+import com._1c.g5.v8.dt.metadata.mdclass.ScriptVariant;
+import com.e1c.v8codestyle.bsl.ModuleStructureSection;
+
+/**
+ * Implementation of {@link IBslModuleRegionsService}
+ *
+ * @author Kuznetsov Nikita
+ */
+public class BslModuleRegionsService
+ implements IBslModuleRegionsService
+{
+ private static final String FORM_NAME = "Form"; //$NON-NLS-1$
+ private static final String GRAPHICAL_SCHEME_NAME = "GraphicalScheme"; //$NON-NLS-1$
+
+ @Override
+ public Pair getRegionInformation(IXtextDocument document,
+ List regionPreprocessors, String suffix, int defaultOffset, ScriptVariant scriptVariant,
+ String eventOwnerRootName, boolean isMain, boolean isCommand, boolean isTable)
+ {
+ String declaredRegionName =
+ getDeclaredRegionName(eventOwnerRootName, isMain, isCommand, isTable, scriptVariant);
+ Map regionOffsets =
+ getRegionOffsets(document, regionPreprocessors, declaredRegionName, scriptVariant);
+ int offset = getRegionOffset(regionOffsets, declaredRegionName, suffix, defaultOffset, scriptVariant);
+ String regionName = null;
+ if (!isRegionExists(regionOffsets, declaredRegionName, suffix))
+ {
+ regionName = suffix.isEmpty() ? declaredRegionName : (declaredRegionName + suffix);
+ }
+ return new Pair<>(offset, regionName);
+ }
+
+ @Override
+ public String wrapByRegion(String content, String regionName, String beginRegion, String endRegion, String space,
+ String lineSeparator)
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(lineSeparator).append(beginRegion).append(space).append(regionName);
+ builder.append(content);
+ builder.append(endRegion);
+ builder.append(lineSeparator);
+ return builder.toString();
+ }
+
+ private Map getRegionOffsets(IXtextDocument document,
+ List regionPreprocessors, String targetRegionName, ScriptVariant scriptVariant)
+ {
+ ModuleStructureSection[] declaredRegionNames = ModuleStructureSection.values();
+ Map regionOffsets = new HashMap<>(declaredRegionNames.length / 2);
+ for (RegionPreprocessor regionPreprocessor : regionPreprocessors)
+ {
+ INode nodeAfter = NodeModelUtils.findActualNodeFor(regionPreprocessor.getItemAfter());
+ if (nodeAfter != null)
+ {
+ String preprocessorRegionName = regionPreprocessor.getName();
+ for (int regionNameIndex = 0; regionNameIndex < declaredRegionNames.length; regionNameIndex++)
+ {
+ ModuleStructureSection moduleStructureSection = declaredRegionNames[regionNameIndex];
+ String declaredRegionName = moduleStructureSection.getName(scriptVariant);
+ if ((preprocessorRegionName != null)
+ && isMatchingRegion(preprocessorRegionName, declaredRegionName))
+ {
+ INode node = NodeModelUtils.findActualNodeFor(regionPreprocessor.getItem());
+ if (node != null)
+ {
+ ModuleRegionInformation moduleRegionInformation = null;
+ if (!preprocessorRegionName.equals(declaredRegionName))
+ {
+ if (!moduleStructureSection.isSuffixed())
+ {
+ continue;
+ }
+ String suffix = getSuffixOfMatchingRegion(preprocessorRegionName, declaredRegionName);
+ moduleRegionInformation = regionOffsets.get(declaredRegionName);
+ if (moduleRegionInformation == null)
+ {
+ moduleRegionInformation = ModuleRegionInformation.create(document, node, nodeAfter);
+ if (moduleRegionInformation == null)
+ {
+ return regionOffsets;
+ }
+ }
+ moduleRegionInformation.addSuffix(suffix, document, node, nodeAfter);
+ }
+ if (moduleRegionInformation == null && !moduleStructureSection.isSuffixed())
+ {
+ moduleRegionInformation = ModuleRegionInformation.create(document, node, nodeAfter);
+ }
+ if (moduleRegionInformation != null)
+ {
+ regionOffsets.put(declaredRegionName, moduleRegionInformation);
+ if ((targetRegionName != null) && targetRegionName.equals(preprocessorRegionName))
+ {
+ return regionOffsets;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return regionOffsets;
+ }
+
+ private int getRegionOffset(Map regionOffsets, String declaredRegionName,
+ String suffix, int defaultOffset, ScriptVariant scriptVariant)
+ {
+ int offset = defaultOffset;
+ boolean createNewRegion = !isRegionExists(regionOffsets, declaredRegionName, suffix);
+ ModuleRegionInformation regionOffset = regionOffsets.get(declaredRegionName);
+ if (regionOffset != null)
+ {
+ if (!suffix.isEmpty())
+ {
+ ModuleRegionInformation suffixedRegionInformation = regionOffset.getInformationBySuffix(suffix);
+ if (suffixedRegionInformation != null)
+ {
+ return suffixedRegionInformation.getBeforeEndOffset();
+ }
+ else if (createNewRegion)
+ {
+ return getNewRegionOffset(regionOffsets, declaredRegionName, suffix, defaultOffset, scriptVariant);
+ }
+ }
+ return regionOffset.getBeforeEndOffset();
+ }
+ if (createNewRegion)
+ {
+ return getNewRegionOffset(regionOffsets, declaredRegionName, suffix, defaultOffset, scriptVariant);
+ }
+ return offset;
+ }
+
+ private boolean isRegionExists(Map regionOffsets, String declaredRegionName,
+ String suffix)
+ {
+ ModuleRegionInformation moduleRegionInformation = regionOffsets.get(declaredRegionName);
+ return (moduleRegionInformation != null)
+ && (suffix.isEmpty() || moduleRegionInformation.getInformationBySuffix(suffix) != null);
+ }
+
+ private int getNewRegionOffset(Map regionOffsets, String regionName,
+ String suffix, int defaultOffset, ScriptVariant scriptVariant)
+ {
+ boolean placeBefore = false;
+ int offset = regionOffsets.isEmpty() ? 0 : defaultOffset;
+ ModuleStructureSection[] declaredRegionNames = ModuleStructureSection.values();
+ for (int regionNameIndex = 0; regionNameIndex < declaredRegionNames.length; regionNameIndex++)
+ {
+ ModuleStructureSection moduleStructuredSection = declaredRegionNames[regionNameIndex];
+ String declaredRegionName = moduleStructuredSection.getName(scriptVariant);
+ if (declaredRegionName.equals(regionName))
+ {
+ placeBefore = true;
+ }
+ ModuleRegionInformation regionInformation = regionOffsets.get(declaredRegionName);
+ if (regionInformation != null)
+ {
+ if (placeBefore && (suffix.isEmpty() || !declaredRegionName.equals(regionName)))
+ {
+ return regionInformation.getStartOffset();
+ }
+ offset = placeBefore ? regionInformation.getStartOffset() : regionInformation.getEndOffset();
+ }
+ }
+ return offset;
+ }
+
+ private String getDeclaredRegionName(String eventOwnerRootName, boolean isMain, boolean isCommand, boolean isTable,
+ ScriptVariant scriptVariant)
+ {
+ if (eventOwnerRootName.equals(FORM_NAME))
+ {
+ return getDeclaredRegionNameForForm(isMain, isCommand, isTable, scriptVariant);
+ }
+ if (eventOwnerRootName.equals(GRAPHICAL_SCHEME_NAME))
+ {
+ return getDeclaredRegionNameForGraphicalScheme(scriptVariant);
+ }
+ return getDefaultRegionName(scriptVariant);
+ }
+
+ private String getDeclaredRegionNameForForm(boolean isMain, boolean isCommand, boolean isTable,
+ ScriptVariant scriptVariant)
+ {
+ if (isMain)
+ {
+ return ModuleStructureSection.FORM_EVENT_HANDLERS.getName(scriptVariant);
+ }
+ else if (isCommand)
+ {
+ return ModuleStructureSection.FORM_COMMAND_EVENT_HANDLERS.getName(scriptVariant);
+ }
+ else if (isTable)
+ {
+ return ModuleStructureSection.FORM_TABLE_ITEMS_EVENT_HANDLERS.getName(scriptVariant);
+ }
+ return ModuleStructureSection.FORM_HEADER_ITEMS_EVENT_HANDLERS.getName(scriptVariant);
+ }
+
+ private String getDeclaredRegionNameForGraphicalScheme(ScriptVariant scriptVariant)
+ {
+ return ModuleStructureSection.EVENT_HANDLERS.getName(scriptVariant);
+ }
+
+ private String getDefaultRegionName(ScriptVariant scriptVariant)
+ {
+ return ModuleStructureSection.PRIVATE.getName(scriptVariant);
+ }
+
+ private String getSuffixOfMatchingRegion(String regionName, String declaredRegionName)
+ {
+ return regionName.substring(declaredRegionName.length(), regionName.length());
+ }
+
+ private boolean isMatchingRegion(String regionName, String declaredRegionName)
+ {
+ int declaredRegionNameLength = declaredRegionName.length();
+ return regionName.length() >= declaredRegionNameLength
+ && regionName.substring(0, declaredRegionNameLength).equals(declaredRegionName);
+ }
+}
diff --git a/bundles/com.e1c.v8codestyle.bsl.ui/src/com/e1c/v8codestyle/internal/bsl/ui/services/ModuleRegionInformation.java b/bundles/com.e1c.v8codestyle.bsl.ui/src/com/e1c/v8codestyle/internal/bsl/ui/services/ModuleRegionInformation.java
new file mode 100644
index 00000000..65b9afce
--- /dev/null
+++ b/bundles/com.e1c.v8codestyle.bsl.ui/src/com/e1c/v8codestyle/internal/bsl/ui/services/ModuleRegionInformation.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright (C) 2023, 1C
+ */
+package com.e1c.v8codestyle.internal.bsl.ui.services;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.xtext.nodemodel.INode;
+
+import com.e1c.v8codestyle.internal.bsl.ui.UiPlugin;
+
+/**
+ * Provides offset and suffixes information of module region
+ *
+ * @author Kuznetsov Nikita
+ */
+public class ModuleRegionInformation
+{
+ private int startOffset;
+ private int endOffset;
+ private int insertOffset;
+
+ private Map suffixes;
+
+ /**
+ * Start offset of module region
+ *
+ * @return offset before region declaration
+ */
+ public int getStartOffset()
+ {
+ return startOffset;
+ }
+
+ /**
+ * End offset of module region
+ *
+ * @return offset after region declaration
+ */
+ public int getEndOffset()
+ {
+ return endOffset;
+ }
+
+ /**
+ * Before end offset inside module region
+ *
+ * @return offset before end of region declaration
+ */
+ public int getBeforeEndOffset()
+ {
+ return insertOffset;
+ }
+
+ /**
+ * Add suffix of the name of this module region
+ *
+ * @param suffix to add to suffixes map of this module region
+ * @param document to update offsets, can't be {@code null}
+ * @param node to update offsets, can't be {@code null}
+ * @param nodeAfter to update offsets, can't be {@code null}
+ */
+ public void addSuffix(String suffix, IDocument document, INode node, INode nodeAfter)
+ {
+ if (suffixes == null)
+ {
+ suffixes = new HashMap<>();
+ }
+ ModuleRegionInformation suffixRegionInformation = create(document, node, nodeAfter);
+ if (suffixRegionInformation != null)
+ {
+ suffixes.put(suffix, suffixRegionInformation);
+ int suffixStartOffset = suffixRegionInformation.getStartOffset();
+ int suffixEndOffset = suffixRegionInformation.getEndOffset();
+ int suffixInsertOffset = suffixRegionInformation.getBeforeEndOffset();
+ if (insertOffset < suffixInsertOffset)
+ {
+ insertOffset = suffixInsertOffset;
+ endOffset = suffixEndOffset;
+ }
+ if (startOffset > suffixStartOffset)
+ {
+ startOffset = suffixStartOffset;
+ }
+ }
+ }
+
+ /**
+ * Is module region has suffixes
+ *
+ * @return {@code true} if module region has suffixes, {@code false} otherwise
+ */
+ public boolean hasSuffixes()
+ {
+ return (suffixes != null);
+ }
+
+ /**
+ * Get module region information by suffix if exists
+ *
+ * @param suffix {@link String} suffix of declared name module region
+ * @return {@link ModuleRegionInformation} if suffix exists, {@code null} otherwise
+ */
+ public ModuleRegionInformation getInformationBySuffix(String suffix)
+ {
+ if (hasSuffixes())
+ {
+ return suffixes.get(suffix);
+ }
+ return null;
+ }
+
+ private ModuleRegionInformation(int startOffset, int endOffset, int insertOffset)
+ {
+ this.startOffset = startOffset;
+ this.endOffset = endOffset;
+ this.insertOffset = insertOffset;
+ }
+
+ /**
+ * Create and calculate module region offsets
+ *
+ * @param document actual {@link IDocument} to get offsets from, can't be {@code null}
+ * @param node {@link INode} to get offsets from, can't be {@code null}
+ * @param nodeAfter {@link INode} to get offsets from, can't be {@code null}
+ * @return module region information with calculated region offsets or {@code null}
+ */
+ public static ModuleRegionInformation create(IDocument document, INode node, INode nodeAfter)
+ {
+ try
+ {
+ int startLine = node.getTotalStartLine() - 1;
+ int startOffset = document.getLineOffset((startLine <= 1) ? 0 : startLine);
+ int endOffset = nodeAfter.getTotalOffset();
+ int insertOffset = document.getLineOffset(document.getLineOfOffset(nodeAfter.getTotalOffset()));
+ return new ModuleRegionInformation(startOffset, endOffset, insertOffset);
+ }
+ catch (BadLocationException ex)
+ {
+ UiPlugin.log(UiPlugin.createErrorStatus("Can't create module region information", ex)); //$NON-NLS-1$
+ }
+ return null;
+ }
+}
diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/ModuleStructureSection.java b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/ModuleStructureSection.java
index 599ea313..31ef3d2c 100644
--- a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/ModuleStructureSection.java
+++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/ModuleStructureSection.java
@@ -15,29 +15,37 @@ package com.e1c.v8codestyle.bsl;
import com._1c.g5.v8.dt.metadata.mdclass.ScriptVariant;
/**
- * Standard module structure dually-named section.
+ * Standard module structure dually-named section;
+ *
Sorted by priority of module regions standard.
*
* @author Dmitriy Marmyshev
*/
public enum ModuleStructureSection
{
- PUBLIC("Public", "ПрограммныйИнтерфейс"), //$NON-NLS-1$ //$NON-NLS-2$
- INTERNAL("Internal", "СлужебныйПрограммныйИнтерфейс"), //$NON-NLS-1$ //$NON-NLS-2$
- PRIVATE("Private", "СлужебныеПроцедурыИФункции"), //$NON-NLS-1$ //$NON-NLS-2$
VARIABLES("Variables", "ОписаниеПеременных"), //$NON-NLS-1$ //$NON-NLS-2$
- INITIALIZE("Initialize", "Инициализация"), //$NON-NLS-1$ //$NON-NLS-2$
+ PUBLIC("Public", "ПрограммныйИнтерфейс"), //$NON-NLS-1$ //$NON-NLS-2$
EVENT_HANDLERS("EventHandlers", "ОбработчикиСобытий"), //$NON-NLS-1$ //$NON-NLS-2$
+ INTERNAL("Internal", "СлужебныйПрограммныйИнтерфейс"), //$NON-NLS-1$ //$NON-NLS-2$
FORM_EVENT_HANDLERS("FormEventHandlers", "ОбработчикиСобытийФормы"), //$NON-NLS-1$ //$NON-NLS-2$
FORM_HEADER_ITEMS_EVENT_HANDLERS("FormHeaderItemsEventHandlers", "ОбработчикиСобытийЭлементовШапкиФормы"), //$NON-NLS-1$ //$NON-NLS-2$
+ FORM_TABLE_ITEMS_EVENT_HANDLERS("FormTableItemsEventHandlers", "ОбработчикиСобытийЭлементовТаблицыФормы", true), //$NON-NLS-1$ //$NON-NLS-2$
FORM_COMMAND_EVENT_HANDLERS("FormCommandsEventHandlers", "ОбработчикиКомандФормы"), //$NON-NLS-1$ //$NON-NLS-2$
- FORM_TABLE_ITEMS_EVENT_HANDLERS("FormTableItemsEventHandlers", "ОбработчикиСобытийЭлементовТаблицыФормы"), //$NON-NLS-1$ //$NON-NLS-2$
+ PRIVATE("Private", "СлужебныеПроцедурыИФункции"), //$NON-NLS-1$ //$NON-NLS-2$
+ INITIALIZE("Initialize", "Инициализация"), //$NON-NLS-1$ //$NON-NLS-2$
DEPRECATED_REGION("Deprecated", "УстаревшиеПроцедурыИФункции"); //$NON-NLS-1$ //$NON-NLS-2$
private final String[] names;
+ private final boolean isSuffixed;
+
+ ModuleStructureSection(String name, String nameRu, boolean isSuffixed)
+ {
+ this.names = new String[] { name, nameRu };
+ this.isSuffixed = isSuffixed;
+ }
ModuleStructureSection(String name, String nameRu)
{
- this.names = new String[] { name, nameRu };
+ this(name, nameRu, false);
}
/**
@@ -61,4 +69,13 @@ public enum ModuleStructureSection
return names[scriptVariant.getValue()];
}
+ /**
+ * Is module region name used only with suffix
+ *
+ * @return true
if region name must be suffixed, false
otherwise
+ */
+ public boolean isSuffixed()
+ {
+ return isSuffixed;
+ }
}
diff --git a/bundles/com.e1c.v8codestyle.bsl/templates/en/command_module.bsl b/bundles/com.e1c.v8codestyle.bsl/templates/en/command_module.bsl
index 67df44fe..8adad148 100644
--- a/bundles/com.e1c.v8codestyle.bsl/templates/en/command_module.bsl
+++ b/bundles/com.e1c.v8codestyle.bsl/templates/en/command_module.bsl
@@ -2,9 +2,7 @@
#Region EventHandlers
// Enter code here.
-
//%CURRENT_CODE%
-
#EndRegion
#Region Private
diff --git a/bundles/com.e1c.v8codestyle.bsl/templates/en/form_module.bsl b/bundles/com.e1c.v8codestyle.bsl/templates/en/form_module.bsl
index f2dda768..d2d1171a 100644
--- a/bundles/com.e1c.v8codestyle.bsl/templates/en/form_module.bsl
+++ b/bundles/com.e1c.v8codestyle.bsl/templates/en/form_module.bsl
@@ -6,9 +6,7 @@
#Region FormEventHandlers
// Enter code here.
-
//%CURRENT_CODE%
-
#EndRegion
#Region FormHeaderItemsEventHandlers
@@ -17,12 +15,6 @@
#EndRegion
-#Region FormTableItemsEventHandlers //
-
-// Enter code here.
-
-#EndRegion
-
#Region FormCommandsEventHandlers
// Enter code here.
diff --git a/bundles/com.e1c.v8codestyle.bsl/templates/ru/command_module.bsl b/bundles/com.e1c.v8codestyle.bsl/templates/ru/command_module.bsl
index 6fcfda8e..1be65a57 100644
--- a/bundles/com.e1c.v8codestyle.bsl/templates/ru/command_module.bsl
+++ b/bundles/com.e1c.v8codestyle.bsl/templates/ru/command_module.bsl
@@ -2,9 +2,7 @@
#Область ОбработчикиСобытий
// Код процедур и функций
-
//%CURRENT_CODE%
-
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
diff --git a/bundles/com.e1c.v8codestyle.bsl/templates/ru/form_module.bsl b/bundles/com.e1c.v8codestyle.bsl/templates/ru/form_module.bsl
index ffedd000..d52a296d 100644
--- a/bundles/com.e1c.v8codestyle.bsl/templates/ru/form_module.bsl
+++ b/bundles/com.e1c.v8codestyle.bsl/templates/ru/form_module.bsl
@@ -6,9 +6,7 @@
#Область ОбработчикиСобытийФормы
// Код процедур и функций
-
//%CURRENT_CODE%
-
#КонецОбласти
#Область ОбработчикиСобытийЭлементовШапкиФормы
@@ -17,12 +15,6 @@
#КонецОбласти
-#Область ОбработчикиСобытийЭлементовТаблицыФормы //<ИмяТаблицыФормы>
-
-// Код процедур и функций
-
-#КонецОбласти
-
#Область ОбработчикиКомандФормы
// Код процедур и функций