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 @@ #КонецОбласти -#Область ОбработчикиСобытийЭлементовТаблицыФормы //<ИмяТаблицыФормы> - -// Код процедур и функций - -#КонецОбласти - #Область ОбработчикиКомандФормы // Код процедур и функций