/*
* Copyright 2011-2017 Kay Stenschke
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kstenschke.shifter.models.shiftableTypes;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.ui.components.JBList;
import com.kstenschke.shifter.resources.StaticTexts;
import com.kstenschke.shifter.utils.UtilsFile;
import com.kstenschke.shifter.utils.UtilsTextual;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.apache.commons.lang.StringUtils.trim;
/**
* Included comment shiftableTypes:
*
* 1. Single-line comment => // ...
* 2. Block comment => /* ... *\/
* 3. HTML comment => <!-- ... -->
*/
public class Comment {
/**
* @param str String to be shifted currently
* @return boolean
*/
public static boolean isComment(String str) {
str = str.trim();
return str.startsWith("//") || isBlockComment(str);
}
public static boolean isBlockComment(String str) {
str = str.trim();
return str.startsWith("/*") && str.endsWith("*/");
}
public static boolean isMultipleSingleLineComments(String str) {
if (!str.contains("\n")) {
return false;
}
String lines[] = str.split("\n");
for (String line : lines) {
if (!trim(line).startsWith("//")) {
return false;
}
}
return true;
}
public static boolean isPhpBlockComment(String str) {
str = str.trim();
return str.startsWith("<?php /*") && str.endsWith("*/ ?>");
}
public static boolean isHtmlComment(String str) {
str = str.trim();
return str.startsWith("<!--") && str.endsWith("-->");
}
/**
* @param str
* @return String
*/
public static String getShifted(String str, String filename, Project project) {
if (filename != null && UtilsFile.isPhpFile(filename) && isPhpBlockComment(str)) {
// PHP Block-comment inside PHP or PHTML: convert to HTML comment
return "<!-- " + str.substring(8, str.length() - 5).trim() + " -->";
}
// Default comment shifting: toggle among single-line and block-comment style
str = str.trim();
if (str.startsWith("//")) {
// Convert single line comment to block comment
return "/*" + str.substring(2) + "*/";
}
str = str.substring(2, str.length() - 2);
// This is a single-lined block comment, otherwise shiftMultiLineBlockCommentInDocument() is called
return "//" + (str.contains("\n")
// Convert block- to single line comment
? str.replace("\n", " ")
: str);
}
/**
* @param str
* @return
*/
public static String getPhpBlockCommentFromHtmlComment(String str) {
return "<?php /* " + str.substring(4, str.length() - 3).trim() + " */ ?>";
}
/**
* Shift multi-lined block comment into single line comment(s)
* Show popup and perform selected shifting mode: join lines into 1 or convert into multiple single line comments
*
* @param str
* @param project
* @param document
* @param offsetStart
* @param offsetEnd
*/
public static void shiftMultiLineBlockCommentInDocument(final String str, final Project project, final Document document, final int offsetStart, final int offsetEnd) {
List<String> shiftOptions = new ArrayList<String>();
shiftOptions.add(StaticTexts.SHIFT_OPTION_MULTILINE_BLOCK_COMMENT_TO_ONE_SINGLE_COMMENT);
shiftOptions.add(StaticTexts.SHIFT_OPTION_MULTILINE_BLOCK_COMMENT_TO_MULTIPLE_SINGLE_COMMENTS);
final Object[] options = shiftOptions.toArray(new String[shiftOptions.size()]);
final JBList modes = new JBList(options);
PopupChooserBuilder popup = JBPopupFactory.getInstance().createListPopupBuilder(modes);
popup.setTitle(StaticTexts.POPUP_TITLE_SHIFT).setItemChoosenCallback(new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
// Callback when item chosen
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
public void run() {
final int index = modes.getSelectedIndex();
String shifted = index == 0
? shiftMultipleBlockCommentLines(str, true)
: shiftMultipleBlockCommentLines(str, false);
document.replaceString(offsetStart, offsetEnd, shifted);
}
},
null, null);
}
});
}
}).setMovable(true).createPopup().showCenteredInCurrentWindow(project);
}
public static void shiftMultipleSingleLineCommentsInDocument(final String str, final Project project, final Document document, final int offsetStart, final int offsetEnd) {
List<String> shiftOptions = new ArrayList<String>();
shiftOptions.add(StaticTexts.SHIFT_OPTION_MULTIPLE_LINE_COMMENTS_MERGE);
shiftOptions.add(StaticTexts.SHIFT_OPTION_MULTIPLE_LINE_COMMENTS_TO_BLOCK_COMMENT);
shiftOptions.add(StaticTexts.SHIFT_OPTION_MULTIPLE_LINE_SORT_ASCENDING);
shiftOptions.add(StaticTexts.SHIFT_OPTION_MULTIPLE_LINE_SORT_DESCENDING);
final Object[] options = shiftOptions.toArray(new String[shiftOptions.size()]);
final JBList modes = new JBList(options);
PopupChooserBuilder popup = JBPopupFactory.getInstance().createListPopupBuilder(modes);
popup.setTitle(StaticTexts.POPUP_TITLE_SHIFT).setItemChoosenCallback(new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
// Callback when item chosen
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
public void run() {
final int index = modes.getSelectedIndex();
String shifted;
switch (index) {
case 0:
shifted = mergeMultipleLineComments(str);
break;
case 1:
shifted = convertMultipleLineCommentsToBlockComment(str);
break;
case 2:
shifted = sortLineComments(str, true);
break;
case 3:
default:
shifted = sortLineComments(str, false);
break;
}
document.replaceString(offsetStart, offsetEnd, shifted);
}
},
null, null);
}
});
}
}).setMovable(true).createPopup().showCenteredInCurrentWindow(project);
}
private static String shiftMultipleBlockCommentLines(String str, boolean merge) {
str = trim(str).substring(2);
String lines[] = str.split("\n");
int index = 0;
String result = "//";
for (String line : lines) {
line = trim(line);
if (line.startsWith("* ")) {
line = line.substring(2);
}
line = trim(line);
if (index == 0 && line.startsWith("*")) {
line = trim(line.substring(1));
}
if (!line.isEmpty()) {
result += merge
? " " + line
: (index == 0 ? "" : "\n") + "// " + line;
}
index++;
}
// Remove trailing "*/"
result = result.substring(0, result.length() - 2);
if (!merge) {
// Remove empty comment lines
result = result.replace("\n//\n", "\n");
if (result.startsWith("//\n")) {
result = result.substring(3);
}
if (result.endsWith("\n// ")) {
result = result.substring(0, result.length() - 4);
}
}
return result;
}
private static String convertMultipleLineCommentsToBlockComment(String str) {
String lines[] = str.split("\n");
String result = "";
int index = 0;
for (String line : lines) {
result += (index == 0 ? "" : "\n") + " * " + trim(trim(line).substring(2));
index++;
}
return "/**\n" + result + "\n */";
}
private static String mergeMultipleLineComments(String str) {
String lines[] = str.split("\n");
String result = "";
int index = 0;
for (String line : lines) {
result += (index == 0 ? "" : " ") + trim(trim(line).substring(2));
index++;
}
return "// " + result;
}
private static String sortLineComments(String str, boolean shiftUp) {
List<String> lines = Arrays.asList(str.split("\n"));
List<String> shiftedList = UtilsTextual.sortLinesNatural(lines, shiftUp);
String result = "";
int index = 0;
for(String line : shiftedList) {
result += (index == 0 ? "" : "\n") + line;
index++;
}
return result;
}
}