├── settings.gradle ├── src ├── main │ ├── resources │ │ ├── images │ │ │ └── px2rwd.gif │ │ ├── intentionDescriptions │ │ │ ├── PX2VHIntention │ │ │ │ ├── description.html │ │ │ │ ├── before.css.template │ │ │ │ └── after.css.template │ │ │ ├── PX2VWIntention │ │ │ │ ├── description.html │ │ │ │ ├── before.css.template │ │ │ │ └── after.css.template │ │ │ ├── PX2REMIntention │ │ │ │ ├── description.html │ │ │ │ ├── after.css.template │ │ │ │ └── before.css.template │ │ │ └── RollbackIntention │ │ │ │ ├── after.css.template │ │ │ │ ├── description.html │ │ │ │ └── before.css.template │ │ └── META-INF │ │ │ └── plugin.xml │ └── java │ │ └── com │ │ └── sunqian │ │ ├── constvalue │ │ ├── ShortCutType.java │ │ ├── MagicValue.java │ │ └── ConstValue.java │ │ ├── utils │ │ ├── ExceptionUtils.java │ │ ├── CollectionUtils.java │ │ ├── StringUtils.java │ │ ├── LogicUtils.java │ │ ├── NumberUtils.java │ │ └── FormatTools.java │ │ ├── completion │ │ ├── PX2RWDCompletion.java │ │ └── PX2RWDProvider.java │ │ ├── intention │ │ ├── IntentionUtils.java │ │ ├── PX2VHIntention.java │ │ ├── PX2VWIntention.java │ │ ├── PX2REMIntention.java │ │ └── RollbackIntention.java │ │ ├── action │ │ ├── ConvertRollback.java │ │ ├── PX2RWDWithFile.java │ │ └── PX2RWDWithLine.java │ │ ├── model │ │ └── ActionPerformer.java │ │ └── settings │ │ ├── ProjectSettingConfig.java │ │ ├── ProjectSettingsPage.java │ │ └── ProjectSettingsPage.form └── test │ └── java │ └── com │ └── sunqian │ └── px2rwd │ └── test │ └── P2RTestApplication.java ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── LICENSE ├── .gitignore ├── gradlew.bat ├── README_CN.md ├── README.md └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'px2rem' 2 | 3 | -------------------------------------------------------------------------------- /src/main/resources/images/px2rwd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunqian1991/px2rwd-intellij-plugin/HEAD/src/main/resources/images/px2rwd.gif -------------------------------------------------------------------------------- /src/test/java/com/sunqian/px2rwd/test/P2RTestApplication.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.px2rwd.test; 2 | 3 | public class P2RTestApplication { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/constvalue/ShortCutType.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.constvalue; 2 | 3 | public enum ShortCutType { 4 | REM,VW,VH,PX,CM 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2VHIntention/description.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This intention converts px to vh. 4 |

5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2VWIntention/description.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This intention converts px to vw. 4 |

5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2REMIntention/description.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This intention converts px to rem. 4 |

5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/RollbackIntention/after.css.template: -------------------------------------------------------------------------------- 1 | .class-name{ 2 | padding: 10px 20px 30px; 3 | } -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/RollbackIntention/description.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | convert rem/vw/vh to px 5 |

6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/RollbackIntention/before.css.template: -------------------------------------------------------------------------------- 1 | .class-name{ 2 | padding: 1rem 1.042vw 2.778vh; 3 | } -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2REMIntention/after.css.template: -------------------------------------------------------------------------------- 1 | .class-name{ 2 | width: 10rem; 3 | height: 10rem; 4 | padding: 1rem 2rem 3rem 4rem; 5 | } -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2REMIntention/before.css.template: -------------------------------------------------------------------------------- 1 | .class-name{ 2 | width: 100px; 3 | height: 100px; 4 | padding: 10px 20px 30px 40px; 5 | } -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2VHIntention/before.css.template: -------------------------------------------------------------------------------- 1 | .class-name{ 2 | width: 100px; 3 | height: 100px; 4 | padding: 10px 20px 30px 40px; 5 | } -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2VWIntention/before.css.template: -------------------------------------------------------------------------------- 1 | .class-name{ 2 | width: 100px; 3 | height: 100px; 4 | padding: 10px 20px 30px 40px; 5 | } -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2VHIntention/after.css.template: -------------------------------------------------------------------------------- 1 | .class-name{ 2 | width: 9.259vh; 3 | height: 9.259vh; 4 | padding: 0.926vh 1.852vh 2.778vh 3.704vh; 5 | } -------------------------------------------------------------------------------- /src/main/resources/intentionDescriptions/PX2VWIntention/after.css.template: -------------------------------------------------------------------------------- 1 | .class-name{ 2 | width: 5.208vw; 3 | height: 5.208vw; 4 | padding: 0.521vw 1.042vw 1.563vw 2.083vw; 5 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jun 06 09:45:37 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/utils/ExceptionUtils.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | public class ExceptionUtils { 8 | 9 | public static String getRootMessage(final Throwable throwable) { 10 | return Optional.ofNullable(getRootCause(throwable)).map(Throwable::getMessage).orElse(""); 11 | } 12 | 13 | public static Throwable getRootCause(final Throwable throwable) { 14 | final List list = getThrowableList(throwable); 15 | return list.isEmpty() ? null : list.get(list.size() - 1); 16 | } 17 | 18 | public static List getThrowableList(Throwable throwable) { 19 | final List list = new ArrayList<>(); 20 | while (throwable != null && !list.contains(throwable)) { 21 | list.add(throwable); 22 | throwable = throwable.getCause(); 23 | } 24 | return list; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/completion/PX2RWDCompletion.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.completion; 2 | 3 | import com.intellij.codeInsight.completion.*; 4 | import com.intellij.patterns.PlatformPatterns; 5 | import com.intellij.psi.css.impl.CssElementTypes; 6 | import com.sunqian.utils.LogicUtils; 7 | 8 | import java.util.Arrays; 9 | 10 | import static com.sunqian.constvalue.MagicValue.AUTO_COMPLETION_TAG; 11 | import static com.sunqian.constvalue.MagicValue.TO_RWD_TIPS; 12 | 13 | /** 14 | * 代码自动完成 15 | * 16 | * @author sunqian 17 | * date 2019/6/21 18 | */ 19 | public class PX2RWDCompletion extends CompletionContributor { 20 | public PX2RWDCompletion() { 21 | extend( 22 | CompletionType.BASIC, 23 | PlatformPatterns.psiElement(CssElementTypes.CSS_IDENT).withParent(PlatformPatterns.psiElement(CssElementTypes.CSS_NUMBER_TERM)), 24 | new PX2RWDProvider(LogicUtils.getLogic().generateObject(new String[TO_RWD_TIPS.length], tips -> Arrays.fill(tips, AUTO_COMPLETION_TAG))) 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/com/sunqian/utils/CollectionUtils.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.function.Function; 7 | import java.util.stream.Stream; 8 | 9 | /** 10 | * 集合工具类 11 | * 12 | * @author sunqian 13 | * @date 2019/6/6 14 | */ 15 | @SuppressWarnings({"unused"}) 16 | public class CollectionUtils { 17 | 18 | private volatile static CollectionUtils collectionUtils; 19 | 20 | public static CollectionUtils getCollections() { 21 | return Optional.ofNullable(collectionUtils).orElseGet(() -> { 22 | synchronized (CollectionUtils.class) { 23 | return Optional.ofNullable(collectionUtils).orElseGet(CollectionUtils::new); 24 | } 25 | }); 26 | } 27 | 28 | public R getFirst(List list, Function function) { 29 | return Optional.ofNullable(list).filter(l -> l.size() > 0).map(l -> function.apply(l.get(0))).orElse(null); 30 | } 31 | 32 | @SafeVarargs 33 | public final List concatenate(List... lists) { 34 | return LogicUtils.getLogic().generateObject(new ArrayList<>(), list -> Stream.of(lists).forEach(list::addAll)); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/intention/IntentionUtils.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.intention; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.PsiElement; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Objects; 9 | import java.util.Optional; 10 | import java.util.regex.Pattern; 11 | 12 | import static com.sunqian.constvalue.MagicValue.STYLE_PATTERN_FORMAT; 13 | import static com.sunqian.constvalue.MagicValue.STYLE_SHEET_LANGUAGE_ID; 14 | 15 | class IntentionUtils { 16 | 17 | static boolean isAvailable(Editor editor, @NotNull PsiElement element){ 18 | return Optional.of(element.getLanguage()).filter(language -> Objects.equals(language.getID(), STYLE_SHEET_LANGUAGE_ID)).map(language -> Optional.of(editor.getDocument().getLineNumber(editor.getCaretModel().getOffset())).map(lineNum -> 19 | Optional.of(editor.getDocument().getText( 20 | new TextRange(editor.getDocument().getLineStartOffset(lineNum), editor.getDocument().getLineEndOffset(lineNum)) 21 | )).map(text -> Pattern.compile(STYLE_PATTERN_FORMAT).matcher(text.toLowerCase()).matches()).get() 22 | ).orElse(false)).orElse(false); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/action/ConvertRollback.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.action; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.CommonDataKeys; 6 | import com.intellij.openapi.actionSystem.LangDataKeys; 7 | import com.sunqian.model.ActionPerformer; 8 | import com.sunqian.utils.FormatTools; 9 | import com.sunqian.utils.LogicUtils; 10 | import com.sunqian.utils.StringUtils; 11 | 12 | import java.util.Objects; 13 | import java.util.Optional; 14 | 15 | import static com.sunqian.constvalue.MagicValue.*; 16 | import static com.sunqian.constvalue.MagicValue.STYLUS_LANGUAGE_ID; 17 | 18 | /** 19 | * back action 20 | * 21 | * @author sunqian 22 | * date 2019/6/25 23 | */ 24 | public class ConvertRollback extends AnAction { 25 | 26 | @Override 27 | public void actionPerformed(AnActionEvent anActionEvent) { 28 | LogicUtils.getLogic().conOrEnd(anActionEvent.getData(LangDataKeys.PSI_FILE), file -> Objects.nonNull(file) && StringUtils.containsAny(file.getLanguage().getID(), STYLE_SHEET_LANGUAGE_ID, LESS_LANGUAGE_ID, SASS_LANGUAGE_ID, HTML_LANGUAGE_ID, SCSS_LANGUAGE_ID, STYLUS_LANGUAGE_ID), file -> 29 | Optional.ofNullable(ActionPerformer.getActionPerformer(anActionEvent.getRequiredData(CommonDataKeys.PROJECT), anActionEvent.getRequiredData(CommonDataKeys.EDITOR))).ifPresent(ap -> 30 | Optional.of(FormatTools.getFormatTools(ap.getConstValue())).ifPresent(formatTools -> 31 | ap.getCaretModel().runForEachCaret(caret -> formatTools.rollbackStyle(ap, ap.getDocument().getLineNumber(caret.getOffset()))) 32 | ) 33 | ) 34 | ); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/action/PX2RWDWithFile.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.action; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.CommonDataKeys; 6 | import com.intellij.openapi.actionSystem.LangDataKeys; 7 | import com.sunqian.model.ActionPerformer; 8 | import com.sunqian.utils.FormatTools; 9 | import com.sunqian.utils.LogicUtils; 10 | import com.sunqian.utils.StringUtils; 11 | 12 | import java.util.Objects; 13 | import java.util.Optional; 14 | import java.util.stream.IntStream; 15 | 16 | import static com.sunqian.constvalue.MagicValue.*; 17 | 18 | /** 19 | * PX转响应式布局样式单位----按文件处理 20 | * 21 | * @author sunqian 22 | * date 2019/6/10 23 | */ 24 | @SuppressWarnings("duplicate") 25 | public class PX2RWDWithFile extends AnAction { 26 | 27 | @Override 28 | public void actionPerformed(AnActionEvent anActionEvent) { 29 | LogicUtils.getLogic().conOrEnd(anActionEvent.getData(LangDataKeys.PSI_FILE), file -> Objects.nonNull(file) && StringUtils.containsAny(file.getLanguage().getID(), STYLE_SHEET_LANGUAGE_ID, LESS_LANGUAGE_ID), file -> 30 | Optional.of(ActionPerformer.getActionPerformer(anActionEvent.getRequiredData(CommonDataKeys.PROJECT), anActionEvent.getRequiredData(CommonDataKeys.EDITOR))).ifPresent(actionPerformer -> 31 | Optional.of(FormatTools.getFormatTools(actionPerformer.getConstValue())).ifPresent(formatTools -> 32 | IntStream.range(0, actionPerformer.getDocument().getLineCount()).forEach(lineNum -> 33 | formatTools.formatLineCode(actionPerformer, lineNum, actionPerformer.getConstValue().getShortCutType()) 34 | ) 35 | ) 36 | ) 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/intention/PX2VHIntention.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.intention; 2 | 3 | import com.intellij.codeInsight.intention.IntentionAction; 4 | import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.util.IncorrectOperationException; 9 | import com.sunqian.constvalue.ShortCutType; 10 | import com.sunqian.model.ActionPerformer; 11 | import com.sunqian.utils.FormatTools; 12 | import org.jetbrains.annotations.Nls; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Optional; 16 | 17 | /** 18 | * intention action类 19 | * 20 | * @author sunqian 21 | * date 2019/6/21 22 | */ 23 | public class PX2VHIntention extends PsiElementBaseIntentionAction implements IntentionAction { 24 | @Override 25 | public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { 26 | Optional.ofNullable(ActionPerformer.getActionPerformer(project, editor)).ifPresent(ap -> 27 | Optional.of(FormatTools.getFormatTools(ap.getConstValue())).ifPresent(formatTools -> 28 | formatTools.formatLineCode(ap, ShortCutType.VH) 29 | ) 30 | ); 31 | } 32 | 33 | @Override 34 | public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { 35 | return ActionPerformer.getActionPerformer(project, editor).getConstValue().getVhIntention() && IntentionUtils.isAvailable(editor, element); 36 | } 37 | 38 | @Nls(capitalization = Nls.Capitalization.Sentence) 39 | @NotNull 40 | @Override 41 | public String getFamilyName() { 42 | return "Px to vh converter"; 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public String getText() { 48 | return "Px to vh"; 49 | } 50 | 51 | @Override 52 | public boolean startInWriteAction() { 53 | return false; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/intention/PX2VWIntention.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.intention; 2 | 3 | import com.intellij.codeInsight.intention.IntentionAction; 4 | import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.util.IncorrectOperationException; 9 | import com.sunqian.constvalue.ShortCutType; 10 | import com.sunqian.model.ActionPerformer; 11 | import com.sunqian.utils.FormatTools; 12 | import org.jetbrains.annotations.Nls; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Optional; 16 | 17 | /** 18 | * intention action类 19 | * 20 | * @author sunqian 21 | * date 2019/6/21 22 | */ 23 | public class PX2VWIntention extends PsiElementBaseIntentionAction implements IntentionAction { 24 | @Override 25 | public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { 26 | Optional.ofNullable(ActionPerformer.getActionPerformer(project, editor)).ifPresent(ap -> 27 | Optional.of(FormatTools.getFormatTools(ap.getConstValue())).ifPresent(formatTools -> 28 | formatTools.formatLineCode(ap, ShortCutType.VW) 29 | ) 30 | ); 31 | } 32 | 33 | @Override 34 | public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { 35 | return ActionPerformer.getActionPerformer(project, editor).getConstValue().getVwIntention() && IntentionUtils.isAvailable(editor, element); 36 | } 37 | 38 | @Nls(capitalization = Nls.Capitalization.Sentence) 39 | @NotNull 40 | @Override 41 | public String getFamilyName() { 42 | return "Px to vw converter"; 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public String getText() { 48 | return "Px to vw"; 49 | } 50 | 51 | @Override 52 | public boolean startInWriteAction() { 53 | return false; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/intention/PX2REMIntention.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.intention; 2 | 3 | import com.intellij.codeInsight.intention.IntentionAction; 4 | import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.util.IncorrectOperationException; 9 | import com.sunqian.constvalue.ShortCutType; 10 | import com.sunqian.model.ActionPerformer; 11 | import com.sunqian.utils.FormatTools; 12 | import org.jetbrains.annotations.Nls; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Optional; 16 | 17 | /** 18 | * intention action类 19 | * 20 | * @author sunqian 21 | * date 2019/6/21 22 | */ 23 | public class PX2REMIntention extends PsiElementBaseIntentionAction implements IntentionAction { 24 | @Override 25 | public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { 26 | Optional.ofNullable(ActionPerformer.getActionPerformer(project, editor)).ifPresent(ap -> 27 | Optional.of(FormatTools.getFormatTools(ap.getConstValue())).ifPresent(formatTools -> 28 | formatTools.formatLineCode(ap, ShortCutType.REM) 29 | ) 30 | ); 31 | } 32 | 33 | @Override 34 | public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { 35 | return ActionPerformer.getActionPerformer(project, editor).getConstValue().getRemIntention() && IntentionUtils.isAvailable(editor, element); 36 | } 37 | 38 | @Nls(capitalization = Nls.Capitalization.Sentence) 39 | @NotNull 40 | @Override 41 | public String getFamilyName() { 42 | return "Px to rem converter"; 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public String getText() { 48 | return "Px to rem"; 49 | } 50 | 51 | @Override 52 | public boolean startInWriteAction() { 53 | return false; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/model/ActionPerformer.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.model; 2 | 3 | import com.intellij.openapi.editor.CaretModel; 4 | import com.intellij.openapi.editor.Document; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.editor.SelectionModel; 7 | import com.intellij.openapi.project.Project; 8 | import com.sunqian.constvalue.ConstValue; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | /** 13 | * 转换特定对象管理类 14 | * 15 | * @author sunqian 16 | * date 2019/6/10 17 | */ 18 | @Data 19 | @NoArgsConstructor 20 | public class ActionPerformer { 21 | 22 | private static volatile ActionPerformer actionPerformer; 23 | 24 | private Project project; 25 | private Editor editor; 26 | private Document document; 27 | private SelectionModel selectionModel; 28 | private CaretModel caretModel; 29 | private ConstValue constValue; 30 | 31 | private ActionPerformer(Project project, Editor editor) { 32 | this.project = project; 33 | this.editor = editor; 34 | } 35 | 36 | public static ActionPerformer getActionPerformer(Project project, Editor editor) { 37 | if (actionPerformer == null || actionPerformer.getProject() != project || actionPerformer.getEditor() != editor) { 38 | synchronized (ActionPerformer.class) { 39 | if (actionPerformer == null || actionPerformer.getProject() != project || actionPerformer.getEditor() != editor) { 40 | actionPerformer = new ActionPerformer(project, editor); 41 | } 42 | } 43 | } 44 | return actionPerformer; 45 | } 46 | 47 | public Document getDocument() { 48 | return this.editor.getDocument(); 49 | } 50 | 51 | public CaretModel getCaretModel() { 52 | return this.editor.getCaretModel(); 53 | } 54 | 55 | public ConstValue getConstValue() { 56 | return ConstValue.getInstance(); 57 | } 58 | 59 | public SelectionModel getSelectionModel() { 60 | return this.editor.getSelectionModel(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.war 15 | *.nar 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | .idea 24 | .idea/*.xml 25 | /.idea 26 | /.idea/*.xml 27 | overlays 28 | *.iml 29 | **/.iml* 30 | *.ipr 31 | *.iws 32 | out 33 | 34 | # Eclipse Project files 35 | .classpath 36 | .project 37 | .settings/ 38 | */.settings/ 39 | 40 | bin/ 41 | gen/ 42 | 43 | .DS_Store 44 | Thumbs.db 45 | 46 | *.bak 47 | *.tem 48 | *.temp 49 | #.swp 50 | 51 | **/.class 52 | **/.classpath 53 | **/.project 54 | */target/ 55 | target/ 56 | 57 | 58 | .gradle 59 | /build/ 60 | 61 | # Ignore Gradle GUI config 62 | gradle-app.setting 63 | 64 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 65 | !gradle-wrapper.jar 66 | 67 | # Cache of project 68 | .gradletasknamecache 69 | 70 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 71 | # gradle/wrapper/gradle-wrapper.properties 72 | 73 | 74 | # Compiled class file 75 | *.class 76 | 77 | # Log file 78 | *.log 79 | 80 | # BlueJ files 81 | *.ctxt 82 | 83 | # Mobile Tools for Java (J2ME) 84 | .mtj.tmp/ 85 | 86 | # Package Files # 87 | *.jar 88 | *.war 89 | *.nar 90 | *.ear 91 | *.zip 92 | *.tar.gz 93 | *.rar 94 | 95 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 96 | hs_err_pid* 97 | 98 | # Windows thumbnail cache files 99 | Thumbs.db 100 | Thumbs.db:encryptable 101 | ehthumbs.db 102 | ehthumbs_vista.db 103 | 104 | # Dump file 105 | *.stackdump 106 | 107 | # Folder config file 108 | [Dd]esktop.ini 109 | 110 | # Recycle Bin used on file shares 111 | $RECYCLE.BIN/ 112 | 113 | # Windows Installer files 114 | *.cab 115 | *.msi 116 | *.msix 117 | *.msm 118 | *.msp 119 | 120 | # Windows shortcuts 121 | *.lnk 122 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/constvalue/MagicValue.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.constvalue; 2 | 3 | import com.sunqian.utils.LogicUtils; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * 魔法值库 10 | * 11 | * @author sunqian 12 | * date 2019/6/6 13 | */ 14 | public class MagicValue { 15 | 16 | public static final String PX_STYLE_TAG = "px"; 17 | 18 | public static final String REM_STYLE_TAG = "rem"; 19 | 20 | public static final String VW_STYLE_TAG = "vw"; 21 | 22 | public static final String VH_STYLE_TAG = "vh"; 23 | 24 | public static final String NUMBER_PATTERN_FORMULA = "-?[0-9]*(\\.[0-9]+)?"; 25 | 26 | public static final String NULL_STRING = ""; 27 | 28 | public static final String STYLE_PATTERN_FORMAT = "^[\\w\\W]+\\:[\\w\\W]*\\d+(\\.{1}(\\d+))?px[\\w\\W]*$"; 29 | 30 | public static final String STYLE_SHEET_LANGUAGE_ID = "CSS"; 31 | 32 | public static final String LESS_LANGUAGE_ID = "LESS"; 33 | 34 | public static final String SASS_LANGUAGE_ID = "SASS"; 35 | 36 | public static final String HTML_LANGUAGE_ID = "HTML"; 37 | 38 | public static final String SCSS_LANGUAGE_ID = "SCSS"; 39 | 40 | public static final String STYLUS_LANGUAGE_ID = "Stylus"; 41 | 42 | public static final String BLANK_STRING = " "; 43 | 44 | public static final String COLON_STRING = ":"; 45 | 46 | public static final String AUTO_COMPLETION_TAG = "px"; 47 | 48 | public static final String PLUGIN_NAME = "Px to Rwd"; 49 | 50 | public static final String TO_REM_TIP = " to rem"; 51 | 52 | public static final String TO_VW_TIP = " to vw"; 53 | 54 | public static final String TO_VH_TIP = " to vh"; 55 | 56 | public static final String[] TO_RWD_TIPS = new String[]{TO_VW_TIP, TO_VH_TIP, TO_REM_TIP}; 57 | 58 | public static final String[] RWD_TIPS = new String[]{REM_STYLE_TAG, VW_STYLE_TAG, VH_STYLE_TAG}; 59 | 60 | public static final String PREFIX_TAIL_TIPS = " px to "; 61 | 62 | public static final Map STYLE_TAG_TYPE = LogicUtils.getLogic().generateObject(new HashMap<>(), map -> 63 | map.put(ShortCutType.REM, REM_STYLE_TAG), map -> 64 | map.put(ShortCutType.VW, VW_STYLE_TAG), map -> 65 | map.put(ShortCutType.VH, VH_STYLE_TAG), map -> 66 | map.put(ShortCutType.PX, PX_STYLE_TAG) 67 | ); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/action/PX2RWDWithLine.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.action; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.CommonDataKeys; 6 | import com.intellij.openapi.actionSystem.LangDataKeys; 7 | import com.sunqian.model.ActionPerformer; 8 | import com.sunqian.utils.FormatTools; 9 | import com.sunqian.utils.LogicUtils; 10 | import com.sunqian.utils.StringUtils; 11 | 12 | import java.util.HashMap; 13 | import java.util.Objects; 14 | import java.util.Optional; 15 | import java.util.function.Consumer; 16 | 17 | import static com.sunqian.constvalue.MagicValue.*; 18 | 19 | /** 20 | * PX转响应式布局样式单位----按行处理 21 | *

22 | * 0 处理整行 23 | * 1 处理选中的文字 24 | * 25 | * @author sunqian 26 | * @date 2019/6/10 27 | */ 28 | @SuppressWarnings("ALL") 29 | public class PX2RWDWithLine extends AnAction { 30 | 31 | @Override 32 | public void actionPerformed(AnActionEvent anActionEvent) { 33 | Optional.ofNullable(ActionPerformer.getActionPerformer(anActionEvent.getRequiredData(CommonDataKeys.PROJECT), anActionEvent.getRequiredData(CommonDataKeys.EDITOR))).ifPresent(ap -> 34 | LogicUtils.getLogic().conOrEnd(anActionEvent.getData(LangDataKeys.PSI_FILE), file -> Objects.nonNull(file) && (!ap.getConstValue().getOnlyCssFiles() || StringUtils.containsAny(file.getLanguage().getID(), STYLE_SHEET_LANGUAGE_ID, LESS_LANGUAGE_ID, SASS_LANGUAGE_ID, HTML_LANGUAGE_ID, SCSS_LANGUAGE_ID, STYLUS_LANGUAGE_ID)), file -> 35 | Optional.of(FormatTools.getFormatTools(ap.getConstValue())).ifPresent(formatTools -> 36 | LogicUtils.getLogic().generateObject(new HashMap>(), map -> { 37 | map.put("0", actionPerformer -> formatTools.formatLineCode(actionPerformer, actionPerformer.getConstValue().getShortCutType())); 38 | map.put("1", actionPerformer -> formatTools.formatSelectCode(actionPerformer, actionPerformer.getConstValue().getShortCutType())); 39 | }).get(Optional.ofNullable(ap.getSelectionModel().getSelectedText()).filter(text -> !Objects.equals(text, "")).map(text -> "1").orElse("0")).accept(ap) 40 | ) 41 | ) 42 | ); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | ## PX2RWD-Intellij-Plugin 2 | 3 | [![](https://img.shields.io/badge/license-MIT-000000.svg)](https://github.com/sunqian1991/px2rwd-intellij-plugin/blob/master/LICENSE) 4 | [![](https://img.shields.io/jetbrains/plugin/v/11187.svg)](https://plugins.jetbrains.com/plugin/11187-px2rem) 5 | [![](https://img.shields.io/jetbrains/plugin/d/11187.svg)](https://plugins.jetbrains.com/plugin/11187-px2rem) 6 | [![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) 7 | 8 | #### 项目介绍 9 | 10 | 这是一个转换css文件中的px单位为rem/vw/vh等自适应设计页面(Responsive Web Design)单位的插件,主要适用于idea和webstorm. 11 | 12 | #### 安装 13 | 14 | 此插件已经提交到插件仓库中,可以直接在插件市场 `file - settings - plugins - marketplace` 中搜索 `px2rem`. 15 | 16 | 你也可以直接从 [release](https://github.com/sunqian1991/px2rwd-intellij-plugin/releases) 库中下载最新的版本,然后从本地安装。 17 | 18 | #### 联系我 19 | 20 | 有任何问题或者建议都可以联系我。 21 | 22 | QQ: 991637393 23 | 24 | Email: sunqian1991@gmail.com 25 | 26 | 27 | #### 使用方法 28 | 29 | 目前提供了三种方式来转换:快捷键、代码意图提示(Alt + Enter)、代码自动完成提示 30 | 31 | 1. 快捷键 32 | 33 | 默认的快捷键是Alt + d,在插件配置页面中可以看到快捷键部分有3种转换类型的选项,分别是rem,vw,vh,选择其中的一个来使用快捷键转换,需要注意的是,需要在选项下方的值配置中设置对应的基值。 34 | 只能选择一种转换类型来使用快捷键转换单位。同时也可以重新设置快捷键。需要注意的是,跟之前的版本相比,默认的快捷键修改了,修改的原因主要是原来的Shift + d会与大小写冲突。 35 | 36 | 2. code intention 37 | 38 | 选择使用代码意图提示的类型后即可使用已选择的类型来在css文件中通过Alt + Shift来调用code intention来快捷转换单位。未勾选的选项不会出现在文件的code intention列表中。 39 | 40 | 3. code completion 41 | 42 | 选择使用代码自动提示的类型后即可使用已选择的类型来在css文件中快捷转换单位,当输入'px'后,code completion列表中会出现在配置页面中选择的类型名称,选择其中一个类型后即可实现自动完成转换功能。 43 | 44 | 如果觉得这个插件还不错,希望给我加个星,非常感激。 45 | 46 | 使用说明: 47 | 1. File-Settings-Px to Rem进行必要的参数配置 48 | 2. 选择一个需要转换的样式或者将光标移动到一个包含需要转换样式的行中,使用快捷键Alt + d来转换,也可以使用Ctrl + Alt + d来转换整个文件中的可转换样式 49 | 3. 通过code intentionAlt + Enter来转换某一行中涉及到的样式 50 | 4. 通过在输入'px'字符后显示的自动完成提示列表中选择相应的选项来转换相应的样式 51 | 5. 通过快捷键Ctrl + Shift + Alt + d来回退一行内的转换 52 | 53 | #### 注意事项 54 | 在使用插件转换时,**请避免在样式名称中使用值和`px`的组合**,如: 55 | ```css 56 | .test200px{ 57 | ... 58 | } 59 | ``` 60 | 此时`test200px`也会被自动转换,可能会造成极大风险 61 | 62 | 当启用在注释中显示计算过程的功能时,插件会在每次转换的样式的后面添加注释,在一些情况下会极大影响样式的可读性,请谨慎开启!!如: 63 | ```css 64 | .test{ 65 | box-shadow: 0.1rem /* 10 / 100 */ 0.1rem /* 10 / 100 */ 0.1rem /* 10 / 100 */ #000; 66 | width:calc(100% - 0.2rem /* 20 / 100 */); 67 | } 68 | ``` 69 | 70 | 71 | ![image][opt_gif] 72 | 73 | 74 | [opt_gif]:https://github.com/sunqian1991/px2rwd-intellij-plugin/raw/master/src/main/resources/images/px2rwd.gif -------------------------------------------------------------------------------- /src/main/java/com/sunqian/intention/RollbackIntention.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.intention; 2 | 3 | import com.intellij.codeInsight.intention.IntentionAction; 4 | import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.util.TextRange; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.util.IncorrectOperationException; 10 | import com.sunqian.model.ActionPerformer; 11 | import com.sunqian.utils.FormatTools; 12 | import com.sunqian.utils.StringUtils; 13 | import org.jetbrains.annotations.Nls; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.Objects; 17 | import java.util.Optional; 18 | 19 | import static com.sunqian.constvalue.MagicValue.*; 20 | 21 | /** 22 | * intention action类 23 | * 24 | * @author sunqian 25 | * date 2019/6/21 26 | */ 27 | public class RollbackIntention extends PsiElementBaseIntentionAction implements IntentionAction { 28 | @Override 29 | public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { 30 | Optional.ofNullable(ActionPerformer.getActionPerformer(project, editor)).ifPresent(ap -> 31 | Optional.of(FormatTools.getFormatTools(ap.getConstValue())).ifPresent(formatTools -> 32 | ap.getCaretModel().runForEachCaret(caret -> formatTools.rollbackStyle(ap, ap.getDocument().getLineNumber(caret.getOffset()))) 33 | ) 34 | ); 35 | } 36 | 37 | @Override 38 | public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { 39 | return Optional.of(element.getLanguage()).filter(language -> Objects.equals(language.getID(), STYLE_SHEET_LANGUAGE_ID)).map(language -> Optional.of(editor.getDocument().getLineNumber(editor.getCaretModel().getOffset())).flatMap(lineNum -> Optional.of(editor.getDocument().getText( 40 | new TextRange(editor.getDocument().getLineStartOffset(lineNum), editor.getDocument().getLineEndOffset(lineNum)) 41 | )).map(text -> StringUtils.containsAny(text, REM_STYLE_TAG, VW_STYLE_TAG, VH_STYLE_TAG))).orElse(false)).orElse(false); 42 | } 43 | 44 | @Nls(capitalization = Nls.Capitalization.Sentence) 45 | @NotNull 46 | @Override 47 | public String getFamilyName() { 48 | return "Rem/vw/vh to px"; 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public String getText() { 54 | return "Rem/vw/vh to px"; 55 | } 56 | 57 | @Override 58 | public boolean startInWriteAction() { 59 | return false; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## PX2RWD-Intellij-Plugin 2 | 3 | [![](https://img.shields.io/badge/license-MIT-000000.svg)](https://github.com/sunqian1991/px2rwd-intellij-plugin/blob/master/LICENSE) 4 | [![](https://img.shields.io/jetbrains/plugin/v/11187.svg)](https://plugins.jetbrains.com/plugin/11187-px2rem) 5 | [![](https://img.shields.io/jetbrains/plugin/d/11187.svg)](https://plugins.jetbrains.com/plugin/11187-px2rem) 6 | [![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) 7 | 8 | [中文](./README_CN.md) 9 | 10 | #### Description 11 | 12 | This is a tool of converting px to rem/vw/vh in a css/less file by a few settings used in intellij idea or webstorm. 13 | 14 | #### Install 15 | 16 | This plugin has been put in the market and you can find it in `file - settings - plugins - marketplace` by searching a text of `px2rem`. 17 | 18 | You can also download the plugin file which has been put in [release](https://github.com/sunqian1991/px2rwd-intellij-plugin/releases), and install the plugin from disk. 19 | 20 | #### Contact me 21 | 22 | If you have any questions or suggestions, please contact me. 23 | 24 | QQ: 991637393 25 | 26 | email: sunqian1991@gmail.com 27 | 28 | 29 | #### Usage 30 | 31 | there are three ways to convert: short-cut key, code intention, code completion. you can find the settings related to these converting ways in settings page with the path 'File - Settings - Px to Rem' 32 | 33 | 1. short-cut key 34 | 35 | select a converting type from three options of rem, vw, vh in setting page and give a necessary value set below the radio group, then use the default short-cut key of Alt + d to convert a line or a selected text. you can also use the short-cut key of Ctrl + Alt + d to convert in a whole file. 36 | you can only choose one converting type of short-cut key from rem,vw,vg to convert. 37 | btw, you can change the short-cut key in your ide by setting the keymap option. 38 | 39 | 2. code intention 40 | 41 | you can find three code intention types in settings page and you can select if a code intention type works by check the checkbox. 42 | by check a code intention in a line of a css file, which will display by typing Alt + Shift, you can convert the line where the caret displays. 43 | the unselected code intention types in settings page will not show in the list of code intention in a css file 44 | 45 | 3. code completion 46 | 47 | you can also find three code completion types in the settings page and you can select if a code completion type works by check the checkbox. 48 | by typing 'px' in a css file, you can find some code completion types you set in settings page. choose a type then it will give a result of converting. 49 | the unselected code completion types in settings page will not show in the list of code completion in a css file 50 | 51 | Instructions: 52 | 1. find menu at 'File - Settings - Px to Rem', and give some settings. 53 | 2. select a text or move cursor at a line which contains a 'px' value. 54 | 3. use default shortcut key Alt + d to convert px to rem/vw/vh with a line; btw, you can change the shortcut at 'File - Settings - keymap - Plug-ins - px2rem'. 55 | 4. use default shortcut key Ctrl + Alt + d to convert px to rem/vw/vh in a whole file. 56 | 5. use a code intention to convert px to rem/vw/vh in a css file 57 | 6. use a code completion to convert px to rem/vw/vh in a css file 58 | 7. use the short-cut key Ctrl + Shift + Alt + d to rollback the converting within one line 59 | 60 | 61 | ![image][opt_gif] 62 | 63 | 64 | [opt_gif]:https://github.com/sunqian1991/px2rwd-intellij-plugin/raw/master/src/main/resources/images/px2rwd.gif 65 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/constvalue/ConstValue.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.constvalue; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.ServiceManager; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.components.Storage; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import com.sunqian.utils.LogicUtils; 9 | import com.sunqian.utils.NumberUtils; 10 | import lombok.Data; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.Optional; 16 | 17 | /** 18 | * 配置文件参数管理类 19 | * 20 | * @author sunqian 21 | * date 2018/8/8 12:19 22 | */ 23 | @State(name = "px2remForWebStorm", storages = {@Storage("px2remforwebstorm.xml")}) 24 | @Data 25 | public class ConstValue implements PersistentStateComponent { 26 | 27 | /** 28 | * rem比例设置 29 | */ 30 | public String remBaseValue; 31 | 32 | /** 33 | * 是否在注释显示转换过程 34 | */ 35 | public Boolean showCalculationProcess; 36 | 37 | /** 38 | * 转换类型 39 | */ 40 | public ShortCutType shortCutType; 41 | 42 | /** 43 | * vw值 44 | */ 45 | public String widthValue; 46 | 47 | /** 48 | * vh值 49 | */ 50 | public String heightValue; 51 | 52 | /** 53 | * dpi 54 | */ 55 | public String dpiBaseValue; 56 | 57 | /** 58 | * 是否启用code intention 59 | */ 60 | public Boolean remIntention; 61 | 62 | /** 63 | * 是否启用code intention 64 | */ 65 | public Boolean vwIntention; 66 | 67 | /** 68 | * 是否启用code intention 69 | */ 70 | public Boolean vhIntention; 71 | 72 | /** 73 | * 是否启用code completion 74 | */ 75 | public Boolean remCompletion; 76 | 77 | /** 78 | * 是否启用code completion 79 | */ 80 | public Boolean vwCompletion; 81 | 82 | /** 83 | * 是否启用code completion 84 | */ 85 | public Boolean vhCompletion; 86 | 87 | /** 88 | * 仅在css文件类型中使用快捷键转换 89 | */ 90 | public Boolean onlyCssFiles; 91 | 92 | public Boolean getShowCalculationProcess() { 93 | return Optional.ofNullable(showCalculationProcess).orElse(false); 94 | } 95 | 96 | public ShortCutType getShortCutType() { 97 | return Optional.ofNullable(shortCutType).orElse(ShortCutType.REM); 98 | } 99 | 100 | public Double getWidthValue() { 101 | return LogicUtils.getLogic().funOrElse(NumberUtils.toDouble(Optional.ofNullable(widthValue).orElse("1920")), value -> value < 0, value -> 1920d, value -> value); 102 | } 103 | 104 | public Double getHeightValue() { 105 | return LogicUtils.getLogic().funOrElse(NumberUtils.toDouble(Optional.ofNullable(heightValue).orElse("1080")), value -> value < 0, value -> 1080d, value -> value); 106 | } 107 | 108 | public Boolean getRemIntention() { 109 | return Optional.ofNullable(remIntention).orElse(true); 110 | } 111 | 112 | public Boolean getVwIntention() { 113 | return Optional.ofNullable(vwIntention).orElse(false); 114 | } 115 | 116 | public Boolean getVhIntention() { 117 | return Optional.ofNullable(vhIntention).orElse(false); 118 | } 119 | 120 | public Boolean getRemCompletion() { 121 | return Optional.ofNullable(remCompletion).orElse(true); 122 | } 123 | 124 | public Boolean getVwCompletion() { 125 | return Optional.ofNullable(vwCompletion).orElse(false); 126 | } 127 | 128 | public Boolean getVhCompletion() { 129 | return Optional.ofNullable(vhCompletion).orElse(false); 130 | } 131 | 132 | public Boolean getOnlyCssFiles() { 133 | return Optional.ofNullable(onlyCssFiles).orElse(false); 134 | } 135 | 136 | public Map baseValueType() { 137 | return LogicUtils.getLogic().generateObject(new HashMap<>(), map -> 138 | map.put(ShortCutType.REM, this.getRemBaseValue()), map -> 139 | map.put(ShortCutType.VW, this.getWidthValue() / 100), map -> 140 | map.put(ShortCutType.VH, this.getHeightValue() / 100)); 141 | } 142 | 143 | @Override 144 | public ConstValue getState() { 145 | return this; 146 | } 147 | 148 | @Override 149 | public void loadState(ConstValue state) { 150 | XmlSerializerUtil.copyBean(state, this); 151 | } 152 | 153 | public double getRemBaseValue() { 154 | return LogicUtils.getLogic().funOrElse(NumberUtils.toDouble(Optional.ofNullable(this.remBaseValue).orElse("100.0")), value -> value < 0, value -> 100d, value -> value); 155 | } 156 | 157 | @Nullable 158 | public static ConstValue getInstance() { 159 | return ServiceManager.getService(ConstValue.class); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.utils; 2 | 3 | import java.lang.reflect.Array; 4 | 5 | public class StringUtils { 6 | 7 | private static final int NOT_FOUND = -1; 8 | 9 | public static final String EMPTY = ""; 10 | 11 | public static final int INDEX_NOT_FOUND = -1; 12 | 13 | public static boolean isEmpty(final CharSequence cs) { 14 | return cs == null || cs.length() == 0; 15 | } 16 | 17 | public static boolean contains(final CharSequence seq, final int searchChar) { 18 | if (isEmpty(seq)) { 19 | return false; 20 | } 21 | return indexOf(seq, searchChar, 0) >= 0; 22 | } 23 | 24 | public static int indexOf(final CharSequence cs, final int searchChar, int start) { 25 | if (cs instanceof String) { 26 | return ((String) cs).indexOf(searchChar, start); 27 | } 28 | final int sz = cs.length(); 29 | if (start < 0) { 30 | start = 0; 31 | } 32 | if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) { 33 | for (int i = start; i < sz; i++) { 34 | if (cs.charAt(i) == searchChar) { 35 | return i; 36 | } 37 | } 38 | } 39 | //supplementary characters (LANG1300) 40 | if (searchChar <= Character.MAX_CODE_POINT) { 41 | final char[] chars = Character.toChars(searchChar); 42 | for (int i = start; i < sz - 1; i++) { 43 | final char high = cs.charAt(i); 44 | final char low = cs.charAt(i + 1); 45 | if (high == chars[0] && low == chars[1]) { 46 | return i; 47 | } 48 | } 49 | } 50 | return NOT_FOUND; 51 | } 52 | 53 | @SafeVarargs 54 | public static String join(final T... elements) { 55 | return join(elements, null); 56 | } 57 | 58 | public static String join(final Object[] array, final String separator) { 59 | if (array == null) { 60 | return null; 61 | } 62 | return join(array, separator, 0, array.length); 63 | } 64 | 65 | public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) { 66 | if (array == null) { 67 | return null; 68 | } 69 | if (separator == null) { 70 | separator = EMPTY; 71 | } 72 | 73 | // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator)) 74 | // (Assuming that all Strings are roughly equally long) 75 | final int noOfItems = endIndex - startIndex; 76 | if (noOfItems <= 0) { 77 | return EMPTY; 78 | } 79 | 80 | final StringBuilder buf = newStringBuilder(noOfItems); 81 | 82 | for (int i = startIndex; i < endIndex; i++) { 83 | if (i > startIndex) { 84 | buf.append(separator); 85 | } 86 | if (array[i] != null) { 87 | buf.append(array[i]); 88 | } 89 | } 90 | return buf.toString(); 91 | } 92 | 93 | private static StringBuilder newStringBuilder(final int noOfItems) { 94 | return new StringBuilder(noOfItems * 16); 95 | } 96 | 97 | public static boolean containsAny(final CharSequence cs, final CharSequence... searchCharSequences) { 98 | if (isEmpty(cs) || isEmpty(searchCharSequences)) { 99 | return false; 100 | } 101 | for (final CharSequence searchCharSequence : searchCharSequences) { 102 | if (contains(cs, searchCharSequence)) { 103 | return true; 104 | } 105 | } 106 | return false; 107 | } 108 | 109 | public static int indexOf(final CharSequence seq, final CharSequence searchSeq) { 110 | if (seq == null || searchSeq == null) { 111 | return INDEX_NOT_FOUND; 112 | } 113 | return indexOf(seq, searchSeq, 0); 114 | } 115 | 116 | public static boolean contains(final CharSequence seq, final CharSequence searchSeq) { 117 | if (seq == null || searchSeq == null) { 118 | return false; 119 | } 120 | return indexOf(seq, searchSeq, 0) >= 0; 121 | } 122 | 123 | static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) { 124 | return cs.toString().indexOf(searchChar.toString(), start); 125 | } 126 | 127 | public static boolean isEmpty(final Object[] array) { 128 | return getLength(array) == 0; 129 | } 130 | 131 | public static int getLength(final Object array) { 132 | if (array == null) { 133 | return 0; 134 | } 135 | return Array.getLength(array); 136 | } 137 | 138 | public static int lastIndexOf(final CharSequence seq, final CharSequence searchSeq) { 139 | if (seq == null || searchSeq == null) { 140 | return INDEX_NOT_FOUND; 141 | } 142 | return lastIndexOf(seq, searchSeq, seq.length()); 143 | } 144 | 145 | static int lastIndexOf(final CharSequence cs, final CharSequence searchChar, final int start) { 146 | return cs.toString().lastIndexOf(searchChar.toString(), start); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/completion/PX2RWDProvider.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.completion; 2 | 3 | import com.intellij.codeInsight.completion.*; 4 | import com.intellij.codeInsight.lookup.LookupElement; 5 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 6 | import com.intellij.util.ProcessingContext; 7 | import com.sunqian.constvalue.ShortCutType; 8 | import com.sunqian.model.ActionPerformer; 9 | import com.sunqian.utils.FormatTools; 10 | import com.sunqian.utils.LogicUtils; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Arrays; 14 | import java.util.HashMap; 15 | import java.util.Objects; 16 | import java.util.Optional; 17 | import java.util.stream.Collectors; 18 | import java.util.stream.IntStream; 19 | 20 | import static com.sunqian.constvalue.MagicValue.*; 21 | 22 | /** 23 | * 代码自动完成处理类 24 | */ 25 | public class PX2RWDProvider extends CompletionProvider { 26 | private final String[] myItems; 27 | 28 | PX2RWDProvider(String... items) { 29 | myItems = items; 30 | } 31 | 32 | @Override 33 | public void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { 34 | Optional.of(Arrays.stream(TO_RWD_TIPS).filter(item -> filterCompletionItem.test(item, ActionPerformer.getActionPerformer(parameters.getEditor().getProject(), parameters.getEditor()))).collect(Collectors.toList())).ifPresent(tips -> { 35 | result.addAllElements(IntStream.range(0, tips.size()).mapToObj(i -> 36 | LookupElementBuilder.create(myItems[i]) 37 | .withCaseSensitivity(false).withTailText(tips.get(i)) 38 | .withInsertHandler(LogicUtils.getLogic().generateObject(new HashMap>(), map -> 39 | map.put(TO_REM_TIP, PX_REM_HANDLER), map -> 40 | map.put(TO_VW_TIP, PX_VW_HANDLER), map -> 41 | map.put(TO_VH_TIP, PX_VH_HANDLER)).get(tips.get(i)))).collect(Collectors.toList())); 42 | result.addAllElements(Arrays.stream(RWD_TIPS).map(tip -> 43 | LookupElementBuilder.create(tip) 44 | .withCaseSensitivity(false).withTailText(PREFIX_TAIL_TIPS + tip) 45 | .withInsertHandler(LogicUtils.getLogic().generateObject(new HashMap>(), map -> 46 | map.put(REM_STYLE_TAG, REM_HANDLER), map -> 47 | map.put(VW_STYLE_TAG, VW_HANDLER), map -> 48 | map.put(VH_STYLE_TAG, VH_HANDLER)).get(tip))).collect(Collectors.toList())); 49 | }); 50 | } 51 | 52 | private final RwdTipsPredicate filterCompletionItem = (item, actionPerformer) -> 53 | Objects.equals(item, TO_REM_TIP) && (actionPerformer.getConstValue().getRemCompletion()) || 54 | Objects.equals(item, TO_VW_TIP) && (actionPerformer.getConstValue().getVwCompletion()) || 55 | Objects.equals(item, TO_VH_TIP) && (actionPerformer.getConstValue().getVhCompletion()); 56 | 57 | @FunctionalInterface 58 | private interface RwdTipsPredicate { 59 | boolean test(T t, E e); 60 | } 61 | 62 | private static final InsertHandler REM_HANDLER = (context, item) -> 63 | Optional.of(ActionPerformer.getActionPerformer(context.getProject(), context.getEditor())).ifPresent(actionPerformer -> 64 | FormatTools.getFormatTools(actionPerformer.getConstValue()).formatNearCode(actionPerformer, ShortCutType.REM, ShortCutType.REM) 65 | ); 66 | 67 | private static final InsertHandler VW_HANDLER = (context, item) -> 68 | Optional.of(ActionPerformer.getActionPerformer(context.getProject(), context.getEditor())).ifPresent(actionPerformer -> 69 | FormatTools.getFormatTools(actionPerformer.getConstValue()).formatNearCode(actionPerformer, ShortCutType.VW, ShortCutType.VW) 70 | ); 71 | 72 | private static final InsertHandler VH_HANDLER = (context, item) -> 73 | Optional.of(ActionPerformer.getActionPerformer(context.getProject(), context.getEditor())).ifPresent(actionPerformer -> 74 | FormatTools.getFormatTools(actionPerformer.getConstValue()).formatNearCode(actionPerformer, ShortCutType.VH, ShortCutType.VH) 75 | ); 76 | 77 | private static final InsertHandler PX_REM_HANDLER = (context, item) -> 78 | Optional.of(ActionPerformer.getActionPerformer(context.getProject(), context.getEditor())).ifPresent(actionPerformer -> 79 | FormatTools.getFormatTools(actionPerformer.getConstValue()).formatNearCode(actionPerformer, ShortCutType.REM) 80 | ); 81 | 82 | private static final InsertHandler PX_VW_HANDLER = (context, item) -> 83 | Optional.of(ActionPerformer.getActionPerformer(context.getProject(), context.getEditor())).ifPresent(actionPerformer -> 84 | FormatTools.getFormatTools(actionPerformer.getConstValue()).formatNearCode(actionPerformer, ShortCutType.VW) 85 | ); 86 | 87 | private static final InsertHandler PX_VH_HANDLER = (context, item) -> 88 | Optional.of(ActionPerformer.getActionPerformer(context.getProject(), context.getEditor())).ifPresent(actionPerformer -> 89 | FormatTools.getFormatTools(actionPerformer.getConstValue()).formatNearCode(actionPerformer, ShortCutType.VH) 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/utils/LogicUtils.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.utils; 2 | 3 | import lombok.NoArgsConstructor; 4 | 5 | import java.util.Arrays; 6 | import java.util.Optional; 7 | import java.util.function.Consumer; 8 | import java.util.function.Function; 9 | import java.util.function.Predicate; 10 | 11 | /** 12 | * 逻辑判断工具类 13 | * 14 | * @author sunqian 15 | * date 2019/3/22 16 | */ 17 | @SuppressWarnings("unused") 18 | @NoArgsConstructor 19 | public class LogicUtils { 20 | 21 | private volatile static LogicUtils logicUtils; 22 | 23 | public static LogicUtils getLogic() { 24 | return Optional.ofNullable(logicUtils).orElseGet(() -> { 25 | synchronized (LogicUtils.class) { 26 | return Optional.ofNullable(logicUtils).orElseGet(LogicUtils::new); 27 | } 28 | }); 29 | } 30 | 31 | @SafeVarargs 32 | public final boolean or(ExceptionPredicate predicate, T... t) { 33 | return Arrays.stream(t).anyMatch(predicate); 34 | } 35 | 36 | public void conOrElse(T t, ExceptionPredicate predicate, ExceptionConsumer consumerTrue, ExceptionConsumer consumerFalse) { 37 | if (predicate.test(t)) { 38 | consumerTrue.accept(t); 39 | } else { 40 | consumerFalse.accept(t); 41 | } 42 | } 43 | 44 | public R funOrElse(T t, ExceptionPredicate predicate, ExceptionFunction functionTrue, ExceptionFunction functionFalse) { 45 | if (predicate.test(t)) { 46 | return functionTrue.apply(t); 47 | } else { 48 | return functionFalse.apply(t); 49 | } 50 | } 51 | 52 | public void conOrThrow(T t, ExceptionPredicate predicate, ExceptionConsumer consumer, RuntimeException e) { 53 | if (predicate.test(t)) { 54 | consumer.accept(t); 55 | } else { 56 | throw new RuntimeException(ExceptionUtils.getRootMessage(e)); 57 | } 58 | } 59 | 60 | public R funOrThrow(T t, ExceptionPredicate predicate, ExceptionFunction function, RuntimeException e) { 61 | if (predicate.test(t)) { 62 | return function.apply(t); 63 | } else { 64 | throw new RuntimeException(ExceptionUtils.getRootMessage(e)); 65 | } 66 | } 67 | 68 | public void conOrEnd(T t, ExceptionPredicate predicate, ExceptionConsumer consumer) { 69 | if (predicate.test(t)) { 70 | consumer.accept(t); 71 | } 72 | } 73 | 74 | public R funOrEnd(T t, ExceptionPredicate predicate, ExceptionFunction function, R end) { 75 | if (predicate.test(t)) { 76 | return function.apply(t); 77 | } 78 | return end; 79 | } 80 | 81 | T funWithWhile(T t, ExceptionPredicate predicate, ExceptionFunction function) { 82 | while (predicate.test(t)) { 83 | t = function.apply(t); 84 | } 85 | return t; 86 | } 87 | 88 | public void conWithWhile(T t, ExceptionPredicate predicate, ExceptionConsumer consumer) { 89 | while (predicate.test(t)) { 90 | Optional.ofNullable(consumer).ifPresent(con -> con.accept(t)); 91 | } 92 | } 93 | 94 | @SafeVarargs 95 | public final T generateObject(T t, ExceptionConsumer... consumers) { 96 | Arrays.asList(consumers).forEach(consumer -> 97 | Optional.ofNullable(consumer).ifPresent(con -> con.accept(t)) 98 | ); 99 | return t; 100 | } 101 | 102 | @FunctionalInterface 103 | public interface ExceptionConsumer extends Consumer { 104 | 105 | /** 106 | * 重写默认的accept函数 107 | * 108 | * @param t 接受的泛型参数 109 | */ 110 | @Override 111 | default void accept(T t) { 112 | try { 113 | acceptException(t); 114 | } catch (Exception e) { 115 | throw new RuntimeException(ExceptionUtils.getRootMessage(e)); 116 | } 117 | } 118 | 119 | /** 120 | * 可以抛出异常的accept函数 121 | * 122 | * @param t 接受的泛型参数 123 | * @throws Exception 抛出异常 124 | */ 125 | void acceptException(T t) throws Exception; 126 | 127 | } 128 | 129 | @FunctionalInterface 130 | public interface ExceptionFunction extends Function { 131 | 132 | /** 133 | * 重写默认的apply函数 134 | * 135 | * @param t 接受的泛型参数 136 | * @return 返回泛型R 137 | */ 138 | @Override 139 | default R apply(T t) { 140 | try { 141 | return applyException(t); 142 | } catch (Exception e) { 143 | throw new RuntimeException(ExceptionUtils.getRootMessage(e)); 144 | } 145 | } 146 | 147 | /** 148 | * 可以抛出异常的apply函数 149 | * 150 | * @param t 接受的泛型参数 151 | * @return 返回泛型R 152 | * @throws Exception 抛出异常 153 | */ 154 | R applyException(T t) throws Exception; 155 | 156 | } 157 | 158 | @FunctionalInterface 159 | public interface ExceptionPredicate extends Predicate { 160 | 161 | /** 162 | * 重写默认的test函数 163 | * 164 | * @param t 接受的泛型参数 165 | * @return 返回逻辑结果 166 | */ 167 | @Override 168 | default boolean test(T t) { 169 | try { 170 | return testException(t); 171 | } catch (Exception e) { 172 | throw new RuntimeException(ExceptionUtils.getRootMessage(e)); 173 | } 174 | } 175 | 176 | /** 177 | * 可以抛出异常的test函数 178 | * 179 | * @param t 接受的泛型参数 180 | * @return 返回逻辑结果 181 | * @throws Exception 抛出异常 182 | */ 183 | boolean testException(T t) throws Exception; 184 | 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/utils/NumberUtils.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.utils; 2 | 3 | import java.lang.reflect.Array; 4 | 5 | public class NumberUtils { 6 | 7 | public static boolean isCreatable(final String str) { 8 | if (StringUtils.isEmpty(str)) { 9 | return false; 10 | } 11 | final char[] chars = str.toCharArray(); 12 | int sz = chars.length; 13 | boolean hasExp = false; 14 | boolean hasDecPoint = false; 15 | boolean allowSigns = false; 16 | boolean foundDigit = false; 17 | // deal with any possible sign up front 18 | final int start = chars[0] == '-' || chars[0] == '+' ? 1 : 0; 19 | if (sz > start + 1 && chars[start] == '0' && !StringUtils.contains(str, '.')) { // leading 0, skip if is a decimal number 20 | if (chars[start + 1] == 'x' || chars[start + 1] == 'X') { // leading 0x/0X 21 | int i = start + 2; 22 | if (i == sz) { 23 | return false; // str == "0x" 24 | } 25 | // checking hex (it can't be anything else) 26 | for (; i < chars.length; i++) { 27 | if ((chars[i] < '0' || chars[i] > '9') 28 | && (chars[i] < 'a' || chars[i] > 'f') 29 | && (chars[i] < 'A' || chars[i] > 'F')) { 30 | return false; 31 | } 32 | } 33 | return true; 34 | } else if (Character.isDigit(chars[start + 1])) { 35 | // leading 0, but not hex, must be octal 36 | int i = start + 1; 37 | for (; i < chars.length; i++) { 38 | if (chars[i] < '0' || chars[i] > '7') { 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | } 45 | sz--; // don't want to loop to the last char, check it afterwords 46 | // for type qualifiers 47 | int i = start; 48 | // loop to the next to last char or to the last char if we need another digit to 49 | // make a valid number (e.g. chars[0..5] = "1234E") 50 | while (i < sz || i < sz + 1 && allowSigns && !foundDigit) { 51 | if (chars[i] >= '0' && chars[i] <= '9') { 52 | foundDigit = true; 53 | allowSigns = false; 54 | 55 | } else if (chars[i] == '.') { 56 | if (hasDecPoint || hasExp) { 57 | // two decimal points or dec in exponent 58 | return false; 59 | } 60 | hasDecPoint = true; 61 | } else if (chars[i] == 'e' || chars[i] == 'E') { 62 | // we've already taken care of hex. 63 | if (hasExp) { 64 | // two E's 65 | return false; 66 | } 67 | if (!foundDigit) { 68 | return false; 69 | } 70 | hasExp = true; 71 | allowSigns = true; 72 | } else if (chars[i] == '+' || chars[i] == '-') { 73 | if (!allowSigns) { 74 | return false; 75 | } 76 | allowSigns = false; 77 | foundDigit = false; // we need a digit after the E 78 | } else { 79 | return false; 80 | } 81 | i++; 82 | } 83 | if (i < chars.length) { 84 | if (chars[i] >= '0' && chars[i] <= '9') { 85 | // no type qualifier, OK 86 | return true; 87 | } 88 | if (chars[i] == 'e' || chars[i] == 'E') { 89 | // can't have an E at the last byte 90 | return false; 91 | } 92 | if (chars[i] == '.') { 93 | if (hasDecPoint || hasExp) { 94 | // two decimal points or dec in exponent 95 | return false; 96 | } 97 | // single trailing decimal point after non-exponent is ok 98 | return foundDigit; 99 | } 100 | if (!allowSigns 101 | && (chars[i] == 'd' 102 | || chars[i] == 'D' 103 | || chars[i] == 'f' 104 | || chars[i] == 'F')) { 105 | return foundDigit; 106 | } 107 | if (chars[i] == 'l' 108 | || chars[i] == 'L') { 109 | // not allowing L with an exponent or decimal point 110 | return foundDigit && !hasExp && !hasDecPoint; 111 | } 112 | // last character is illegal 113 | return false; 114 | } 115 | // allowSigns is true iff the val ends in 'E' 116 | // found digit it to make sure weird stuff like '.' and '1E-' doesn't pass 117 | return !allowSigns && foundDigit; 118 | } 119 | 120 | public static double toDouble(final String str) { 121 | return toDouble(str, 0.0d); 122 | } 123 | 124 | public static double toDouble(final String str, final double defaultValue) { 125 | if (str == null) { 126 | return defaultValue; 127 | } 128 | try { 129 | return Double.parseDouble(str); 130 | } catch (final NumberFormatException nfe) { 131 | return defaultValue; 132 | } 133 | } 134 | 135 | public static int max(final int... array) { 136 | // Validates input 137 | validateArray(array); 138 | 139 | // Finds and returns max 140 | int max = array[0]; 141 | for (int j = 1; j < array.length; j++) { 142 | if (array[j] > max) { 143 | max = array[j]; 144 | } 145 | } 146 | 147 | return max; 148 | } 149 | 150 | private static void validateArray(final Object array) { 151 | isTrue(array != null, "The Array must not be null"); 152 | isTrue(Array.getLength(array) != 0, "Array cannot be empty."); 153 | } 154 | 155 | public static void isTrue(final boolean expression, final String message, final Object... values) { 156 | if (!expression) { 157 | throw new IllegalArgumentException(String.format(message, values)); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/settings/ProjectSettingConfig.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.settings; 2 | 3 | import com.intellij.openapi.options.SearchableConfigurable; 4 | import com.sunqian.constvalue.ConstValue; 5 | import com.sunqian.constvalue.ShortCutType; 6 | import com.sunqian.utils.LogicUtils; 7 | import com.sunqian.utils.NumberUtils; 8 | import com.sunqian.utils.StringUtils; 9 | import lombok.Data; 10 | import org.jetbrains.annotations.Nls; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import javax.swing.*; 15 | 16 | import java.util.HashMap; 17 | import java.util.Objects; 18 | import java.util.Optional; 19 | 20 | import static com.sunqian.constvalue.MagicValue.PLUGIN_NAME; 21 | 22 | @Data 23 | public class ProjectSettingConfig implements SearchableConfigurable { 24 | 25 | ProjectSettingsPage mainGui; 26 | 27 | private ConstValue constValue; 28 | 29 | public ProjectSettingConfig() { 30 | constValue = ConstValue.getInstance(); 31 | } 32 | 33 | @NotNull 34 | @Override 35 | public String getId() { 36 | return getDisplayName(); 37 | } 38 | 39 | @Nls(capitalization = Nls.Capitalization.Title) 40 | @Override 41 | public String getDisplayName() { 42 | return PLUGIN_NAME; 43 | } 44 | 45 | @Nullable 46 | @Override 47 | public JComponent createComponent() { 48 | mainGui = new ProjectSettingsPage(constValue); 49 | return mainGui.getRootPanel(); 50 | } 51 | 52 | @Nullable 53 | @Override 54 | public String getHelpTopic() { 55 | return null; 56 | } 57 | 58 | @Override 59 | public boolean isModified() { 60 | return ifEdited(); 61 | } 62 | 63 | @Nullable 64 | @Override 65 | public Runnable enableSearch(String option) { 66 | return null; 67 | } 68 | 69 | @Override 70 | public void disposeUIResources() { 71 | mainGui = null; 72 | } 73 | 74 | @Override 75 | public void reset() { 76 | mainGui.getShowCalculationProcessInCheckBox().setSelected(constValue.getShowCalculationProcess()); 77 | mainGui.getRemRadioButton().setSelected(constValue.getShortCutType() == ShortCutType.REM); 78 | mainGui.getVwRadioButton().setSelected(constValue.getShortCutType() == ShortCutType.VW); 79 | mainGui.getVhRadioButton().setSelected(constValue.getShortCutType() == ShortCutType.VH); 80 | mainGui.getCmRadioButton().setSelected(constValue.getShortCutType() == ShortCutType.CM); 81 | mainGui.getRemBaseValue().setText(constValue.getRemBaseValue() + ""); 82 | mainGui.getVwValue().setText(constValue.getWidthValue().toString()); 83 | mainGui.getVhValue().setText(constValue.getHeightValue().toString()); 84 | mainGui.getDpiBaseValue().setText(constValue.getDpiBaseValue()); 85 | mainGui.getRemIntention().setSelected(constValue.getRemIntention()); 86 | mainGui.getVwIntention().setSelected(constValue.getVwIntention()); 87 | mainGui.getVhIntention().setSelected(constValue.getVhIntention()); 88 | mainGui.getRemCompletion().setSelected(constValue.getRemCompletion()); 89 | mainGui.getVwCompletion().setSelected(constValue.getVwCompletion()); 90 | mainGui.getVhCompletion().setSelected(constValue.getVhCompletion()); 91 | mainGui.getOnlyCssFiles().setSelected(constValue.getOnlyCssFiles()); 92 | } 93 | 94 | @Override 95 | public void apply() { 96 | LogicUtils.getLogic().conOrEnd(mainGui.getRemBaseValue().getText(), text -> Objects.nonNull(text) && NumberUtils.isCreatable(text) && NumberUtils.toDouble(text) > 0, text -> constValue.setRemBaseValue(mainGui.getRemBaseValue().getText())); 97 | constValue.setShowCalculationProcess(mainGui.getShowCalculationProcessInCheckBox().isSelected()); 98 | LogicUtils.getLogic().conOrEnd(mainGui.getVwValue().getText(), text -> Objects.nonNull(text) && NumberUtils.isCreatable(text) && NumberUtils.toDouble(text) > 0, text -> constValue.setWidthValue(mainGui.getVwValue().getText())); 99 | LogicUtils.getLogic().conOrEnd(mainGui.getVhValue().getText(), text -> Objects.nonNull(text) && NumberUtils.isCreatable(text) && NumberUtils.toDouble(text) > 0, text -> constValue.setHeightValue(mainGui.getVhValue().getText())); 100 | constValue.setShortCutType(getShortCutType()); 101 | constValue.setRemIntention(mainGui.getRemIntention().isSelected()); 102 | constValue.setVwIntention(mainGui.getVwIntention().isSelected()); 103 | constValue.setVhIntention(mainGui.getVhIntention().isSelected()); 104 | constValue.setRemCompletion(mainGui.getRemCompletion().isSelected()); 105 | constValue.setVwCompletion(mainGui.getVwCompletion().isSelected()); 106 | constValue.setVhCompletion(mainGui.getVhCompletion().isSelected()); 107 | constValue.setOnlyCssFiles(mainGui.getOnlyCssFiles().isSelected()); 108 | } 109 | 110 | private ShortCutType getShortCutType() { 111 | return Optional.ofNullable(LogicUtils.getLogic().generateObject(new HashMap(), map -> 112 | map.put("rem", ShortCutType.REM), map -> 113 | map.put("vw", ShortCutType.VW), map -> 114 | map.put("vh", ShortCutType.VH)).get(StringUtils.join( 115 | mainGui.getRemRadioButton().isSelected() ? "rem" : "", 116 | mainGui.getVwRadioButton().isSelected() ? "vw" : "", 117 | mainGui.getVhRadioButton().isSelected() ? "vh" : "" 118 | ))).orElse(ShortCutType.REM); 119 | } 120 | 121 | private boolean ifEdited() { 122 | return LogicUtils.getLogic().or(flag -> !Objects.equals(flag[0], flag[1]), new Double[]{ 123 | constValue.getRemBaseValue(), NumberUtils.toDouble(mainGui.getRemBaseValue().getText()) 124 | }, new Double[]{ 125 | constValue.getWidthValue(), NumberUtils.toDouble(mainGui.getVwValue().getText()) 126 | }, new Double[]{ 127 | constValue.getHeightValue(), NumberUtils.toDouble(mainGui.getVhValue().getText()) 128 | }, new ShortCutType[]{ 129 | constValue.getShortCutType(), getShortCutType() 130 | }, new Boolean[]{ 131 | constValue.getShowCalculationProcess(), mainGui.getShowCalculationProcessInCheckBox().isSelected() 132 | }, new Boolean[]{ 133 | constValue.getRemIntention(), mainGui.getRemIntention().isSelected() 134 | }, new Boolean[]{ 135 | constValue.getVwIntention(), mainGui.getVwIntention().isSelected() 136 | }, new Boolean[]{ 137 | constValue.getVhIntention(), mainGui.getVhIntention().isSelected() 138 | }, new Boolean[]{ 139 | constValue.getRemCompletion(), mainGui.getRemCompletion().isSelected() 140 | }, new Boolean[]{ 141 | constValue.getVwCompletion(), mainGui.getVwCompletion().isSelected() 142 | }, new Boolean[]{ 143 | constValue.getVhCompletion(), mainGui.getVhCompletion().isSelected() 144 | }, new Boolean[]{ 145 | constValue.getOnlyCssFiles(), mainGui.getOnlyCssFiles().isSelected() 146 | }); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.sunq.action.WebStormPX2REMTools 3 | px2rem 4 | 2.1.5 5 | sunqian 6 | this is a tool of converting px to rem/vw/vh in a css/less file by a few settings.

8 |
9 |

there are three ways to convert: short-cut key, code intention, code completion. you can find the settings related to these converting ways in settings page with the path 'File - Settings - Px to Rem'

10 |
11 |

1. short-cut key

12 |
13 |

select a converting type from three options of rem, vw, vh in setting page and give a necessary value set below the radio group, then use the default short-cut key of Alt + d to convert a line or a selected text. you can also use the short-cut key of Ctrl + Alt + d to convert in a whole file.

14 |

you can only choose one converting type of short-cut key from rem,vw,vg to convert.

15 |

btw, you can change the short-cut key in your ide by setting the keymap option.

16 |
17 |

2. code intention

18 |
19 |

you can find three code intention types in settings page and you can select if a code intention type works by check the checkbox.

20 |

by check a code intention in a line of a css file, which will display by typing Alt + Enter, you can convert the line where the caret displays.

21 |

the unselected code intention types in settings page will not show in the list of code intention in a css file

22 |
23 |

3. code completion

24 |
25 |

you can also find three code completion types in the settings page and you can select if a code completion type works by check the checkbox.

26 |

by typing 'px' in a css file, you can find some code completion types you set in settings page. choose a type then it will give a result of converting.

27 |

the unselected code completion types in settings page will not show in the list of code completion in a css file

28 |
29 |

Instructions:

30 |

1. find menu at 'File - Settings - Px to Rem', and give some settings.

31 |

2. select a text or move cursor at a line which contains a 'px' value.

32 |

3. use default shortcut key Alt + d to convert px to rem/vw/vh with a line; btw, you can change the shortcut at 'File - Settings - keymap - Plug-ins - px2rem'.

33 |

4. use default shortcut key Ctrl + Alt + d to convert px to rem/vw/vh in a whole file.

34 |

5. use a code intention to convert px to rem/vw/vh in a css file

35 |

6. use a code completion to convert px to rem/vw/vh in a css file

36 |

7. use the short-cut key Ctrl + Shift + Alt + d to rollback the converting within one line

37 |
38 |

this plugin only support css, less file type and you should not use it in other file types, because this will bring some unexpected errors.

39 |
40 |

you can find more details in github

41 |
42 |

if this plugin is not bad, you can give me a star in github and I will be very grateful for your support.

43 |
44 |

这是一个转换css文件中的px单位为rem/vw/vh等单位的插件,主要适用于idea和webstorm.

45 |
46 |

目前提供了三种方式来转换:快捷键、代码意图提示(Alt + Enter)、代码自动完成提示

47 |
48 |

1. 快捷键

49 |
50 |

默认的快捷键是Alt + d,在插件配置页面中可以看到快捷键部分有3种转换类型的选项,分别是rem,vw,vh,选择其中的一个来使用快捷键转换,需要注意的是,需要在选项下方的值配置中设置对应的基值。

51 |

只能选择一种转换类型来使用快捷键转换单位。同时也可以重新设置快捷键。需要注意的是,跟之前的版本相比,默认的快捷键修改了,修改的原因主要是原来的'Shift' + 'd'会与大小写冲突。

52 |
53 |

2. code intention

54 |
55 |

选择使用代码意图提示的类型后即可使用已选择的类型来在css文件中通过Alt + Enter来调用code intention来快捷转换单位。未勾选的选项不会出现在文件的code intention列表中。

56 |
57 |

3. code completion

58 |
59 |

选择使用代码自动提示的类型后即可使用已选择的类型来在css文件中快捷转换单位,当输入'px'后,code completion列表中会出现在配置页面中选择的类型名称,选择其中一个类型后即可实现自动完成转换功能。

60 |
61 |

如果觉得这个插件还不错,希望可以在github上给我点个赞,非常感激。

62 |
63 |

插件仅支持css和less两种文件类型,在其他的样式文件类型如sass中也可以使用某一些功能,但可能会引起某些未知的错误,请谨慎使用!

64 |
65 |

使用说明:

66 |

1. File-Settings-Px to Rem进行必要的参数配置

67 |

2. 选择一个需要转换的样式或者将光标移动到一个包含需要转换样式的行中,使用快捷键Alt + d来转换,也可以使用Ctrl + Alt + d来转换整个文件中的可转换样式

68 |

3. 通过code intentionAlt + Enter来转换某一行中涉及到的样式

69 |

4. 通过在输入'px'字符后显示的自动完成提示列表中选择相应的选项来转换相应的样式

70 |

5. 通过快捷键Ctrl + Shift + Alt + d来回退一行内的转换

71 |
72 |

更多请前往github查看

73 | ]]> 74 | 75 | 76 | 77 | com.intellij.modules.lang 78 | com.intellij.css 79 | 80 | 82 | 85 | 86 | 87 | 88 | 92 | 93 | 94 | com.sunqian.intention.PX2REMIntention 95 | CSS 96 | 97 | 98 | com.sunqian.intention.PX2VWIntention 99 | CSS 100 | 101 | 102 | com.sunqian.intention.PX2VHIntention 103 | CSS 104 | 105 | 106 | com.sunqian.intention.RollbackIntention 107 | CSS 108 | 109 | 110 | 111 | 112 | 113 | 114 | 116 | 117 | 118 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/utils/FormatTools.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.utils; 2 | 3 | import com.intellij.openapi.command.WriteCommandAction; 4 | import com.intellij.openapi.editor.CaretModel; 5 | import com.intellij.openapi.editor.Document; 6 | import com.intellij.openapi.util.TextRange; 7 | import com.sunqian.constvalue.ConstValue; 8 | import com.sunqian.constvalue.ShortCutType; 9 | import com.sunqian.model.ActionPerformer; 10 | import lombok.NoArgsConstructor; 11 | import lombok.Setter; 12 | 13 | import java.math.BigDecimal; 14 | import java.text.MessageFormat; 15 | import java.util.Objects; 16 | import java.util.Optional; 17 | import java.util.regex.Pattern; 18 | 19 | import static com.sunqian.constvalue.MagicValue.*; 20 | 21 | /** 22 | * 转换工具类 23 | * 24 | * @author sunqian 25 | * date 2018/12/8 15:39 26 | */ 27 | @NoArgsConstructor 28 | public class FormatTools { 29 | 30 | @Setter 31 | private volatile static FormatTools formatTools; 32 | 33 | private ConstValue constValue; 34 | 35 | private static final Pattern NUMBER_PATTERN = Pattern.compile(NUMBER_PATTERN_FORMULA); 36 | 37 | private FormatTools(ConstValue constValue) { 38 | this.constValue = constValue; 39 | } 40 | 41 | public static FormatTools getFormatTools(ConstValue constValue) { 42 | return Optional.ofNullable(formatTools).orElseGet(() -> { 43 | synchronized (FormatTools.class) { 44 | return Optional.ofNullable(formatTools).orElseGet(() -> new FormatTools(constValue)); 45 | } 46 | }); 47 | } 48 | 49 | private String getFormatText(String ele, ShortCutType shortCutType) { 50 | return Optional.ofNullable(ele).filter(text -> text.contains(PX_STYLE_TAG) && !Objects.equals(NULL_STRING, text)).map(text -> 51 | valueFormat(NumberUtils.toDouble(text.substring(0, text.indexOf(PX_STYLE_TAG)).trim()), shortCutType) 52 | ).orElse(ele); 53 | } 54 | 55 | private String valueFormat(Double px, ShortCutType shortCutType) { 56 | Double baseValue = this.constValue.baseValueType().get(shortCutType); 57 | if (Objects.isNull(baseValue)) { 58 | return px.toString(); 59 | } 60 | Double rem = px / baseValue; 61 | if (check(Double.toString(px), Double.toString(baseValue))) { 62 | return rem.toString().replaceAll("0*$", "").replaceAll("\\.$", "").replaceAll(",", ".") + STYLE_TAG_TYPE.get(shortCutType) + showComment(px, baseValue); 63 | } 64 | return String.format(getAccuracy(baseValue + ""), rem).replaceAll("0*$", "").replaceAll("\\.$", "").replaceAll(",", ".").trim() + STYLE_TAG_TYPE.get(shortCutType) + showComment(px, baseValue); 65 | } 66 | 67 | /** 68 | * 计算回退 69 | * 70 | * @param result 计算结果 71 | * @param shortCutType 类型 72 | * @return 返回回退的结果 73 | */ 74 | private String valueRollback(String result, ShortCutType shortCutType) { 75 | if (!StringUtils.containsAny(result, REM_STYLE_TAG, VW_STYLE_TAG, VH_STYLE_TAG)) { 76 | return result; 77 | } 78 | if (Objects.equals(NULL_STRING, result)) { 79 | return result; 80 | } 81 | Double baseValue = this.constValue.baseValueType().get(shortCutType); 82 | if (Objects.isNull(baseValue)) { 83 | return result; 84 | } 85 | return Math.round(NumberUtils.toDouble(result.substring(0, result.indexOf(STYLE_TAG_TYPE.get(shortCutType))).trim()) * baseValue) + PX_STYLE_TAG; 86 | } 87 | 88 | private String getFormatLine(String content, ShortCutType shortCutType, String styleTag, FormatStyleValue formatStyleValue) { 89 | int index; 90 | while ((index = StringUtils.indexOf(content.toLowerCase(), styleTag)) > -1) { 91 | int startIndex = index; 92 | while (isNumeric(content.substring(startIndex - 1, index)) && startIndex > 0) { 93 | startIndex--; 94 | } 95 | if (startIndex != index) { 96 | String value = content.substring(startIndex, index) + styleTag; 97 | content = content.substring(0, startIndex) + formatStyleValue.apply(value, shortCutType) + content.substring(index + styleTag.length()); 98 | } else { 99 | break; 100 | } 101 | } 102 | 103 | return content; 104 | } 105 | 106 | @FunctionalInterface 107 | private interface FormatStyleValue { 108 | 109 | R apply(String value, ShortCutType shortCutType); 110 | 111 | } 112 | 113 | /** 114 | * 回退转换过程 115 | * 116 | * @param actionPerformer 动作参数 117 | * @param lineNum 行号 118 | */ 119 | public void rollbackStyle(ActionPerformer actionPerformer, int lineNum) { 120 | int lineStartOffset = actionPerformer.getDocument().getLineStartOffset(lineNum); 121 | int lineEndOffset = actionPerformer.getDocument().getLineEndOffset(lineNum); 122 | String lineContent = actionPerformer.getDocument().getText(new TextRange(lineStartOffset, lineEndOffset)); 123 | WriteCommandAction.runWriteCommandAction(actionPerformer.getProject(), () -> 124 | actionPerformer.getDocument().replaceString( 125 | lineStartOffset, 126 | lineEndOffset, 127 | getFormatLine( 128 | getFormatLine( 129 | getFormatLine( 130 | lineContent, 131 | ShortCutType.REM, 132 | REM_STYLE_TAG, 133 | this::valueRollback), 134 | ShortCutType.VW, 135 | VW_STYLE_TAG, 136 | this::valueRollback), 137 | ShortCutType.VH, 138 | VH_STYLE_TAG, 139 | this::valueRollback 140 | ) 141 | ) 142 | ); 143 | } 144 | 145 | /** 146 | * 判断字符串是否为数字 147 | * 148 | * @param str 被判断的字符串 149 | * @return 返回是否是数字 150 | */ 151 | private static boolean isNumeric(String str) { 152 | return NUMBER_PATTERN.matcher(str).matches(); 153 | } 154 | 155 | /** 156 | * 检查是否可以被除尽 157 | * 158 | * @param amount 被除数 159 | * @param count 除数 160 | * @return 是否可以被除尽 161 | */ 162 | @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") 163 | private boolean check(String amount, String count) { 164 | BigDecimal dividend = new BigDecimal(amount.replaceAll("\\.", "")); 165 | BigDecimal divisor = new BigDecimal(count.replaceAll("\\.", "")); 166 | BigDecimal zero = new BigDecimal("0"); 167 | BigDecimal two = new BigDecimal("2"); 168 | BigDecimal five = new BigDecimal("5"); 169 | BigDecimal[] results = dividend.divideAndRemainder(divisor); 170 | if (Objects.equals(zero, results[1])) { 171 | return true; 172 | } 173 | results = dividend.divideAndRemainder( 174 | LogicUtils.getLogic().funWithWhile( 175 | LogicUtils.getLogic().funWithWhile(divisor, m -> 176 | Objects.equals( 177 | m.divideAndRemainder(two)[1], 178 | zero 179 | ), m -> m.divide(two) 180 | ), n -> Objects.equals(n.divideAndRemainder(five)[1], zero), n -> n.divide(five))); 181 | return Objects.equals(results[1], zero); 182 | } 183 | 184 | /** 185 | * 通过注释的方式生成计算过程 186 | * 187 | * @param obj1 被除数 188 | * @param obj2 除数 189 | * @return 返回计算公式 190 | */ 191 | private String showComment(Object obj1, Object obj2) { 192 | return constValue.getShowCalculationProcess() ? " /* " + obj1.toString().replaceAll("0*$", "").replaceAll("\\.$", "") + "/" + obj2.toString().replaceAll("0*$", "").replaceAll("\\.$", "") + " */" : ""; 193 | } 194 | 195 | private String getAccuracy(String remValue) { 196 | return MessageFormat.format("%.{0}f", remValue.substring(0, !StringUtils.contains(remValue, ".") ? remValue.length() : StringUtils.indexOf(remValue, ".")).length() + 1); 197 | } 198 | 199 | /** 200 | * 转换一行的代码 201 | *

202 | * 这里行号取的是当前光标所在的行 203 | * 204 | * @param actionPerformer 获取的动作参数 205 | */ 206 | public void formatLineCode(ActionPerformer actionPerformer, ShortCutType shortCutType) { 207 | actionPerformer.getCaretModel().runForEachCaret(caret -> formatLineCode(actionPerformer, actionPerformer.getDocument().getLineNumber(caret.getOffset()), shortCutType)); 208 | } 209 | 210 | /** 211 | * 转换一行代码中的样式单位 212 | * 213 | * @param actionPerformer 获取的动作参数 214 | * @param lineNum 行号 215 | */ 216 | public void formatLineCode(ActionPerformer actionPerformer, int lineNum, ShortCutType shortCutType) { 217 | // 行起始offset 218 | Optional.of(actionPerformer.getDocument().getLineStartOffset(lineNum)).ifPresent(lineStartOffset -> { 219 | // 行结束offset 220 | Optional.of(actionPerformer.getDocument().getLineEndOffset(lineNum)).ifPresent(lineEndOffset -> 221 | Optional.of(actionPerformer.getDocument().getText(new TextRange(lineStartOffset, lineEndOffset))) 222 | .filter(lineContent -> lineContent.toLowerCase().contains(PX_STYLE_TAG)) 223 | .map(lineContent -> { 224 | WriteCommandAction.runWriteCommandAction(actionPerformer.getProject(), () -> 225 | actionPerformer.getDocument().replaceString( 226 | lineStartOffset, 227 | lineEndOffset, 228 | getFormatLine(lineContent, shortCutType, PX_STYLE_TAG, (value, type) -> getFormatText(value, shortCutType))) 229 | ); 230 | return lineContent; 231 | }) 232 | ); 233 | }); 234 | } 235 | 236 | /** 237 | * 格式化光标处往前最近的一个可格化的长度 238 | * 239 | * @param actionPerformer 获取的动作参数 240 | */ 241 | public void formatNearCode(ActionPerformer actionPerformer, ShortCutType shortCutType, ShortCutType unit) { 242 | Document document = actionPerformer.getDocument(); 243 | CaretModel caretModel = actionPerformer.getCaretModel(); 244 | int lineNum = document.getLineNumber(caretModel.getOffset()); 245 | int lineStartOffset = document.getLineStartOffset(lineNum); 246 | String lineContent = document.getText(new TextRange(lineStartOffset, caretModel.getOffset())); 247 | String content = lineContent.substring(getNearCode(lineContent) + 1, lineContent.length() - STYLE_TAG_TYPE.get(unit).length()).trim(); 248 | formatText( 249 | content, 250 | new int[]{ 251 | caretModel.getOffset() - content.length() - STYLE_TAG_TYPE.get(unit).length(), 252 | caretModel.getOffset(), 253 | }, 254 | actionPerformer, 255 | shortCutType); 256 | } 257 | 258 | public void formatNearCode(ActionPerformer actionPerformer, ShortCutType shortCutType) { 259 | formatNearCode(actionPerformer, shortCutType, ShortCutType.PX); 260 | } 261 | 262 | /** 263 | * 取一个长度单位的起始index 264 | * 265 | * @param content 待处理文本 266 | * @return 返回文本中包含的一个长度单位的起始index 267 | */ 268 | private int getNearCode(String content) { 269 | return NumberUtils.max(StringUtils.lastIndexOf(content, COLON_STRING), StringUtils.lastIndexOf(content, BLANK_STRING)); 270 | } 271 | 272 | /** 273 | * 转换选择的代码 274 | * 275 | * @param actionPerformer 获取的动作参数 276 | */ 277 | public void formatSelectCode(ActionPerformer actionPerformer, ShortCutType shortCutType) { 278 | // 光标选择的文字 279 | String selectTexts = actionPerformer.getSelectionModel().getSelectedText(true); 280 | if (Objects.isNull(selectTexts)) { 281 | return; 282 | } 283 | String[] testArray = selectTexts.split("\n"); 284 | int[] starts = actionPerformer.getSelectionModel().getBlockSelectionStarts(); 285 | int[] ends = actionPerformer.getSelectionModel().getBlockSelectionEnds(); 286 | if (testArray.length != starts.length || testArray.length != ends.length) { 287 | return; 288 | } 289 | for (int i = 0; i < testArray.length; i++) { 290 | String selectText = testArray[i]; 291 | if (selectText.contains(PX_STYLE_TAG)) { 292 | formatText( 293 | selectText.substring(0, selectText.indexOf(PX_STYLE_TAG)), 294 | new int[]{ 295 | starts[i], 296 | ends[i] 297 | }, 298 | actionPerformer, 299 | shortCutType); 300 | starts = actionPerformer.getSelectionModel().getBlockSelectionStarts(); 301 | ends = actionPerformer.getSelectionModel().getBlockSelectionEnds(); 302 | } 303 | } 304 | } 305 | 306 | /** 307 | * 格式化指定的style文本 308 | * 309 | * @param style style文本 310 | * @param position 起止index 311 | * @param actionPerformer 动作参数 312 | */ 313 | private void formatText(String style, int[] position, ActionPerformer actionPerformer, ShortCutType shortCutType) { 314 | Optional.of(style).filter(FormatTools::isNumeric).ifPresent(text -> 315 | WriteCommandAction.runWriteCommandAction(actionPerformer.getProject(), () -> 316 | actionPerformer.getDocument().replaceString( 317 | position[0], 318 | position[1], 319 | getFormatText(style + PX_STYLE_TAG, shortCutType)) 320 | ) 321 | ); 322 | } 323 | 324 | } 325 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/settings/ProjectSettingsPage.java: -------------------------------------------------------------------------------- 1 | package com.sunqian.settings; 2 | 3 | import com.intellij.uiDesigner.core.GridConstraints; 4 | import com.intellij.uiDesigner.core.GridLayoutManager; 5 | import com.intellij.uiDesigner.core.Spacer; 6 | import com.jgoodies.forms.layout.CellConstraints; 7 | import com.jgoodies.forms.layout.FormLayout; 8 | import com.sunqian.constvalue.ConstValue; 9 | import com.sunqian.constvalue.ShortCutType; 10 | import com.sunqian.utils.LogicUtils; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | import javax.swing.*; 15 | import java.awt.*; 16 | import java.util.HashMap; 17 | import java.util.function.Consumer; 18 | 19 | @Data 20 | @NoArgsConstructor 21 | public class ProjectSettingsPage { 22 | private JCheckBox showCalculationProcessInCheckBox; 23 | private JTextField remBaseValue; 24 | private JTextField vwValue; 25 | private JTextField vhValue; 26 | private JRadioButton remRadioButton; 27 | private JRadioButton vwRadioButton; 28 | private JRadioButton vhRadioButton; 29 | private JPanel panel; 30 | private JCheckBox remIntention; 31 | private JCheckBox vwIntention; 32 | private JCheckBox vhIntention; 33 | private JCheckBox remCompletion; 34 | private JCheckBox vwCompletion; 35 | private JCheckBox vhCompletion; 36 | private JCheckBox onlyCssFiles; 37 | private JRadioButton cmRadioButton; 38 | private JTextField dpiBaseValue; 39 | private ButtonGroup buttonGroup; 40 | 41 | private ConstValue constValue; 42 | 43 | ProjectSettingsPage(ConstValue constValue) { 44 | this.constValue = constValue; 45 | } 46 | 47 | private void setShowParams() { 48 | remBaseValue.setText(constValue.getRemBaseValue() + ""); 49 | vwValue.setText(constValue.getWidthValue().toString()); 50 | vhValue.setText(constValue.getHeightValue().toString()); 51 | LogicUtils.getLogic().generateObject(new HashMap>(), map -> 52 | map.put(ShortCutType.REM, type -> remRadioButton.setSelected(true)), map -> 53 | map.put(ShortCutType.VW, type -> vwRadioButton.setSelected(true)), map -> 54 | map.put(ShortCutType.VH, type -> vhRadioButton.setSelected(true)), map -> 55 | map.put(ShortCutType.CM, type -> cmRadioButton.setSelected(true))).get(constValue.getShortCutType()).accept(null); 56 | showCalculationProcessInCheckBox.setSelected(constValue.getShowCalculationProcess()); 57 | remIntention.setSelected(constValue.getRemIntention()); 58 | vwIntention.setSelected(constValue.getVwIntention()); 59 | vhIntention.setSelected(constValue.getVhIntention()); 60 | remCompletion.setSelected(constValue.getRemCompletion()); 61 | vwCompletion.setSelected(constValue.getVwCompletion()); 62 | vhCompletion.setSelected(constValue.getVhCompletion()); 63 | onlyCssFiles.setSelected(constValue.getOnlyCssFiles()); 64 | } 65 | 66 | JPanel getRootPanel() { 67 | setShowParams(); 68 | return panel; 69 | } 70 | 71 | { 72 | // GUI initializer generated by IntelliJ IDEA GUI Designer 73 | // >>> IMPORTANT!! <<< 74 | // DO NOT EDIT OR ADD ANY CODE HERE! 75 | $$$setupUI$$$(); 76 | } 77 | 78 | /** 79 | * Method generated by IntelliJ IDEA GUI Designer 80 | * >>> IMPORTANT!! <<< 81 | * DO NOT edit this method OR call it in your code! 82 | * 83 | * @noinspection ALL 84 | */ 85 | private void $$$setupUI$$$() { 86 | panel = new JPanel(); 87 | panel.setLayout(new GridLayoutManager(5, 3, new Insets(0, 0, 0, 0), -1, -1)); 88 | panel.setMaximumSize(new Dimension(600, 300)); 89 | panel.setBorder(BorderFactory.createTitledBorder("")); 90 | final JPanel panel1 = new JPanel(); 91 | panel1.setLayout(new GridLayoutManager(3, 2, new Insets(0, 0, 10, 0), -1, -1)); 92 | panel.add(panel1, new GridConstraints(0, 0, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 93 | panel1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), "")); 94 | final JPanel panel2 = new JPanel(); 95 | panel2.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1)); 96 | panel1.add(panel2, new GridConstraints(2, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 97 | panel2.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "short-cut")); 98 | remRadioButton = new JRadioButton(); 99 | remRadioButton.setEnabled(true); 100 | remRadioButton.setHideActionText(false); 101 | remRadioButton.setSelected(true); 102 | remRadioButton.setText("rem"); 103 | panel2.add(remRadioButton, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 104 | vwRadioButton = new JRadioButton(); 105 | vwRadioButton.setEnabled(true); 106 | vwRadioButton.setSelected(false); 107 | vwRadioButton.setText("vw"); 108 | panel2.add(vwRadioButton, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 109 | vhRadioButton = new JRadioButton(); 110 | vhRadioButton.setEnabled(true); 111 | vhRadioButton.setText("vh"); 112 | panel2.add(vhRadioButton, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 113 | final Spacer spacer1 = new Spacer(); 114 | panel2.add(spacer1, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 115 | final JLabel label1 = new JLabel(); 116 | label1.setText("general"); 117 | panel1.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 118 | final JSeparator separator1 = new JSeparator(); 119 | panel1.add(separator1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 120 | showCalculationProcessInCheckBox = new JCheckBox(); 121 | showCalculationProcessInCheckBox.setText("show calculation process in comment block"); 122 | panel1.add(showCalculationProcessInCheckBox, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 123 | final JPanel panel3 = new JPanel(); 124 | panel3.setLayout(new GridLayoutManager(3, 2, new Insets(0, 0, 10, 0), -1, -1)); 125 | panel.add(panel3, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 126 | panel3.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), "")); 127 | final JPanel panel4 = new JPanel(); 128 | panel4.setLayout(new GridLayoutManager(1, 5, new Insets(0, 0, 0, 0), -1, -1)); 129 | panel3.add(panel4, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_NORTH, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 130 | panel4.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "vw/vh")); 131 | final JLabel label2 = new JLabel(); 132 | label2.setText("width:"); 133 | panel4.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 134 | vwValue = new JTextField(); 135 | panel4.add(vwValue, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); 136 | vhValue = new JTextField(); 137 | panel4.add(vhValue, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); 138 | final Spacer spacer2 = new Spacer(); 139 | panel4.add(spacer2, new GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 140 | final JLabel label3 = new JLabel(); 141 | label3.setText("height:"); 142 | panel4.add(label3, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 143 | final JPanel panel5 = new JPanel(); 144 | panel5.setLayout(new FormLayout("fill:d:noGrow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow", "center:d:noGrow")); 145 | panel3.add(panel5, new GridConstraints(2, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 146 | panel5.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "rem")); 147 | final JLabel label4 = new JLabel(); 148 | label4.setText("root fontsize:"); 149 | CellConstraints cc = new CellConstraints(); 150 | panel5.add(label4, cc.xy(1, 1)); 151 | remBaseValue = new JTextField(); 152 | panel5.add(remBaseValue, cc.xy(3, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); 153 | final Spacer spacer3 = new Spacer(); 154 | panel5.add(spacer3, cc.xy(5, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); 155 | final JLabel label5 = new JLabel(); 156 | label5.setText("values"); 157 | panel3.add(label5, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 158 | final JSeparator separator2 = new JSeparator(); 159 | panel3.add(separator2, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 160 | final Spacer spacer4 = new Spacer(); 161 | panel.add(spacer4, new GridConstraints(4, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 162 | final JPanel panel6 = new JPanel(); 163 | panel6.setLayout(new GridLayoutManager(2, 5, new Insets(0, 0, 10, 0), -1, -1)); 164 | panel.add(panel6, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 165 | panel6.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), null)); 166 | final JLabel label6 = new JLabel(); 167 | label6.setText("code intention"); 168 | panel6.add(label6, new GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 169 | final JSeparator separator3 = new JSeparator(); 170 | panel6.add(separator3, new GridConstraints(0, 2, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 171 | remIntention = new JCheckBox(); 172 | remIntention.setText("rem"); 173 | panel6.add(remIntention, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 174 | final Spacer spacer5 = new Spacer(); 175 | panel6.add(spacer5, new GridConstraints(1, 3, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 176 | vwIntention = new JCheckBox(); 177 | vwIntention.setText("vw"); 178 | panel6.add(vwIntention, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 179 | vhIntention = new JCheckBox(); 180 | vhIntention.setText("vh"); 181 | panel6.add(vhIntention, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 182 | final JPanel panel7 = new JPanel(); 183 | panel7.setLayout(new GridLayoutManager(2, 5, new Insets(0, 0, 10, 0), -1, -1)); 184 | panel.add(panel7, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 185 | final JLabel label7 = new JLabel(); 186 | label7.setText("code completion"); 187 | panel7.add(label7, new GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 188 | final JSeparator separator4 = new JSeparator(); 189 | panel7.add(separator4, new GridConstraints(0, 2, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 190 | remCompletion = new JCheckBox(); 191 | remCompletion.setText("rem "); 192 | panel7.add(remCompletion, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 193 | final Spacer spacer6 = new Spacer(); 194 | panel7.add(spacer6, new GridConstraints(1, 3, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 195 | vwCompletion = new JCheckBox(); 196 | vwCompletion.setText("vw"); 197 | panel7.add(vwCompletion, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 198 | vhCompletion = new JCheckBox(); 199 | vhCompletion.setText("vh"); 200 | panel7.add(vhCompletion, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 201 | ButtonGroup buttonGroup; 202 | buttonGroup = new ButtonGroup(); 203 | buttonGroup.add(remRadioButton); 204 | buttonGroup.add(vwRadioButton); 205 | buttonGroup.add(vhRadioButton); 206 | } 207 | 208 | /** 209 | * @noinspection ALL 210 | */ 211 | public JComponent $$$getRootComponent$$$() { 212 | return panel; 213 | } 214 | 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/com/sunqian/settings/ProjectSettingsPage.form: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 |
368 | --------------------------------------------------------------------------------