Compare commits

...

10 Commits

15 changed files with 160 additions and 176 deletions

View File

@ -26,10 +26,8 @@ tasks.getByName<org.jetbrains.intellij.tasks.PatchPluginXmlTask>("patchPluginXml
<h3>[1.2]</h3> <h3>[1.2]</h3>
<ul> <ul>
<li>Single New menu action to access filename template configurations</li>
<li>Simplify template configuration with placeholder auto completion</li> <li>Simplify template configuration with placeholder auto completion</li>
<li>New templates are active by default</li> <li>New templates are active by default</li>
<li>Combine name and description into title</li>
</ul> </ul>
<h3>[1.1]</h3> <h3>[1.1]</h3>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,4 +1,4 @@
kotlin.code.style=official kotlin.code.style=official
pluginVersion = 1.2.0-211 pluginVersion = 1.2.0-192
platformVersion = 2021.1 platformVersion = 2019.2

View File

@ -3,121 +3,30 @@ package de.idetools.filename_template
import com.intellij.icons.AllIcons import com.intellij.icons.AllIcons
import com.intellij.ide.IdeBundle import com.intellij.ide.IdeBundle
import com.intellij.ide.actions.CreateFileAction import com.intellij.ide.actions.CreateFileAction
import com.intellij.ide.ui.newItemPopup.NewItemPopupUtil
import com.intellij.lang.LangBundle
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.InputValidatorEx import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.Trinity
import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import de.idetools.filename_template.placeholders.* import de.idetools.filename_template.placeholders.PlaceholderProcessor
import de.idetools.filename_template.settings.FilenameTemplateSettingsState
import java.awt.event.InputEvent
import java.util.function.Consumer
import kotlin.math.max
class CreateFileWithFilenameTemplateAction : class CreateFileWithFilenameTemplateAction(
CreateFileAction("Filename Template", "Create a new file based on template", AllIcons.Actions.AddFile) { text: String,
description: String,
private val placeholderProcessor: PlaceholderProcessor
) :
CreateFileAction(text, description, AllIcons.FileTypes.Unknown) {
private val placeholders: List<Placeholder> = listOf( override fun invokeDialog(project: Project, directory: PsiDirectory): Array<PsiElement> {
EpochPlaceholder(),
DatePlaceholder(),
ProjectPlaceholder(),
SequencePlaceholder(),
HostPlaceholder(),
UserPlaceholder(),
DefaultPlaceholder()
)
override fun update(e: AnActionEvent) {
if (FilenameTemplateSettingsState.instance.filenameTemplates.isEmpty()) {
e.presentation.isEnabledAndVisible = false
} else {
super.update(e)
}
}
override fun invokeDialog(
project: Project,
directory: PsiDirectory,
elementsConsumer: Consumer<Array<PsiElement?>?>
) {
val settings: FilenameTemplateSettingsState = FilenameTemplateSettingsState.instance
settings.filenameTemplates.forEach {
if (it.title.isBlank()) {
it.title = if (it.description.isBlank()) it.name else "${it.name} - ${it.description}"
}
it.name = ""
it.description = ""
}
val templates = settings.filenameTemplates
.filter { it.isActive }
.map {
Trinity(it.title, AllIcons.Actions.AddFile, processTemplate(it.template, project, directory))
}
val validator: MyInputValidator = MyValidator(project, directory) val validator: MyInputValidator = MyValidator(project, directory)
val contentPanel = CreateWithFilenameTemplateDialogPanel(templates) val processedTemplate = placeholderProcessor.processTemplate(project, directory)
val nameField = contentPanel.textField Messages.showInputDialog(
val popup = NewItemPopupUtil.createNewItemPopup(IdeBundle.message("title.new.file"), contentPanel, nameField) project,
IdeBundle.message("prompt.enter.new.file.name"),
contentPanel.setApplyAction { event: InputEvent? -> IdeBundle.message("title.new.file"),
val name = nameField.text null,
if (validator.checkInput(name) && validator.canClose(name)) { processedTemplate.filename,
popup.closeOk(event) validator
elementsConsumer.accept(validator.createdElements) )
} else { return validator.createdElements
val errorMessage =
if (validator is InputValidatorEx)
(validator as InputValidatorEx).getErrorText(name)
else
LangBundle.message("incorrect.name")
contentPanel.setError(errorMessage)
}
}
popup.showCenteredInCurrentWindow(project)
}
private fun processTemplate(template: String, project: Project, directory: PsiDirectory): String {
var placeholderLevel = 0
var placeholder = ""
val context = PlaceholderContext(project, directory)
for (character in template) {
when (character) {
'{' -> {
if (0 < placeholderLevel) {
placeholder += character
}
placeholderLevel += 1
}
'}' -> {
if (placeholderLevel == 1) {
context.filename += placeholders
.first { it.supports(placeholder) }
.process(placeholder, context)
placeholder = ""
}
placeholderLevel = max(0, placeholderLevel - 1)
if (0 < placeholderLevel) {
placeholder += character
}
}
else -> {
if (0 < placeholderLevel) {
placeholder += character
} else {
context.filename += character
}
}
}
}
return context.filename
} }
} }

View File

@ -1,32 +0,0 @@
package de.idetools.filename_template
import com.intellij.ide.actions.newclass.CreateWithTemplatesDialogPanel
import com.intellij.openapi.util.Trinity
import javax.swing.Icon
class CreateWithFilenameTemplateDialogPanel(templates: List<Trinity<String, Icon, String>>) :
CreateWithTemplatesDialogPanel(templates, templates.first().first) {
init {
updateSelectedText()
myTemplatesList.addListSelectionListener { updateSelectedText() }
}
private fun updateSelectedText() {
val selectedValue = myTemplatesList.selectedValue
if (selectedValue != null) {
var filename = selectedValue.third
val caretPosition = determineCaretPosition(filename)
filename = filename.replace("{|}", "")
myTextField.text = filename
nameField.caretPosition = caretPosition
}
}
private fun determineCaretPosition(filename: String): Int = when {
filename.indexOf("{|}") != -1 -> filename.indexOf("{|}")
filename.indexOfFirst { it == '.' } != -1 -> filename.indexOfFirst { it == '.' }
else -> filename.length
}
}

View File

@ -0,0 +1,58 @@
package de.idetools.filename_template
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.Anchor
import com.intellij.openapi.actionSystem.Constraints
import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import de.idetools.filename_template.placeholders.DatePlaceholder
import de.idetools.filename_template.placeholders.DefaultPlaceholder
import de.idetools.filename_template.placeholders.EpochPlaceholder
import de.idetools.filename_template.placeholders.HostPlaceholder
import de.idetools.filename_template.placeholders.PlaceholderProcessor
import de.idetools.filename_template.placeholders.ProjectPlaceholder
import de.idetools.filename_template.placeholders.SequencePlaceholder
import de.idetools.filename_template.placeholders.UserPlaceholder
import de.idetools.filename_template.settings.FilenameTemplateSettingsState
class InitializeFilenameTemplateActivity : StartupActivity {
override fun runActivity(project: Project) {
initializeActions()
}
internal fun initializeActions() {
val settings: FilenameTemplateSettingsState = FilenameTemplateSettingsState.instance
val placeholders = listOf(
EpochPlaceholder(),
DatePlaceholder(),
ProjectPlaceholder(),
HostPlaceholder(),
SequencePlaceholder(),
UserPlaceholder(),
DefaultPlaceholder()
)
val actionManager = ActionManager.getInstance()
val newGroup = actionManager.getAction("NewGroup") as DefaultActionGroup
actionManager.getActionIds("FilenameTemplate").forEach {
newGroup.remove(actionManager.getAction(it))
actionManager.unregisterAction(it)
}
var anchorId = "NewFile"
settings.filenameTemplates.filter { it.isActive }.forEach {
val action = CreateFileWithFilenameTemplateAction(
it.name,
it.description,
PlaceholderProcessor(it.template, placeholders)
)
val actionId = "FilenameTemplate.${it.name}"
actionManager.registerAction(actionId, action)
newGroup.addAction(action, Constraints(Anchor.AFTER, anchorId))
anchorId = actionId
}
}
}

View File

@ -0,0 +1,57 @@
package de.idetools.filename_template.placeholders
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDirectory
import kotlin.math.max
class PlaceholderProcessor(private val template: String, private val placeholders: List<Placeholder>) {
fun processTemplate(project: Project, directory: PsiDirectory): ProcessedTemplate {
var placeholderLevel = 0
var placeholder = ""
val context = PlaceholderContext(project, directory)
for (character in template) {
when (character) {
'{' -> {
if (0 < placeholderLevel) {
placeholder += character
}
placeholderLevel += 1
}
'}' -> {
if (placeholderLevel == 1) {
context.filename += placeholders
.first { it.supports(placeholder) }
.process(placeholder, context)
placeholder = ""
}
placeholderLevel = max(0, placeholderLevel - 1)
if (0 < placeholderLevel) {
placeholder += character
}
}
else -> {
if (0 < placeholderLevel) {
placeholder += character
} else {
context.filename += character
}
}
}
}
val caretPosition = when {
context.filename.indexOf("{|}") != -1 -> context.filename.indexOf("{|}")
context.filename.indexOfFirst { it == '.' } != -1 -> context.filename.indexOfFirst { it == '.' }
else -> context.filename.length
}
context.filename = context.filename.replace("{|}", "")
return ProcessedTemplate(context.filename, caretPosition)
}
}

View File

@ -2,6 +2,7 @@ package de.idetools.filename_template.settings
import com.intellij.openapi.options.Configurable import com.intellij.openapi.options.Configurable
import com.intellij.openapi.options.ConfigurationException import com.intellij.openapi.options.ConfigurationException
import de.idetools.filename_template.InitializeFilenameTemplateActivity
import org.jetbrains.annotations.Nls import org.jetbrains.annotations.Nls
import javax.swing.JComponent import javax.swing.JComponent
@ -11,16 +12,8 @@ class FilenameTemplateConfigurable : Configurable {
init { init {
val settings: FilenameTemplateSettingsState = FilenameTemplateSettingsState.instance val settings: FilenameTemplateSettingsState = FilenameTemplateSettingsState.instance
settings.filenameTemplates.forEach {
if (it.title.isBlank()) {
it.title = if (it.description.isBlank()) it.name else "${it.name} - ${it.description}"
}
it.name = ""
it.description = ""
}
component = FilenameTemplateSettingsComponent(ArrayList(settings.filenameTemplates.map { component = FilenameTemplateSettingsComponent(ArrayList(settings.filenameTemplates.map {
FilenameTemplateModel(it.isActive, it.title, it.name, it.description, it.template) FilenameTemplateModel(it.isActive, it.name, it.description, it.template)
})) }))
} }
@ -39,8 +32,9 @@ class FilenameTemplateConfigurable : Configurable {
return settings.filenameTemplates.size != component.filenameTemplates.size || return settings.filenameTemplates.size != component.filenameTemplates.size ||
settings.filenameTemplates.withIndex().any { settings.filenameTemplates.withIndex().any {
component.filenameTemplateTable.getValueAt(it.index, 0) != it.value.isActive component.filenameTemplateTable.getValueAt(it.index, 0) != it.value.isActive
|| component.filenameTemplateTable.getValueAt(it.index, 1) != it.value.title || component.filenameTemplateTable.getValueAt(it.index, 1) != it.value.name
|| component.filenameTemplateTable.getValueAt(it.index, 2) != it.value.template || component.filenameTemplateTable.getValueAt(it.index, 2) != it.value.description
|| component.filenameTemplateTable.getValueAt(it.index, 3) != it.value.template
} }
} }
@ -51,12 +45,12 @@ class FilenameTemplateConfigurable : Configurable {
settings.filenameTemplates.addAll(component.filenameTemplates.map { settings.filenameTemplates.addAll(component.filenameTemplates.map {
FilenameTemplateModel( FilenameTemplateModel(
it.isActive, it.isActive,
it.title,
it.name, it.name,
it.description, it.description,
it.template it.template
) )
}) })
InitializeFilenameTemplateActivity().initializeActions()
} }
override fun reset() { override fun reset() {
@ -65,7 +59,6 @@ class FilenameTemplateConfigurable : Configurable {
component.filenameTemplates.addAll(settings.filenameTemplates.map { component.filenameTemplates.addAll(settings.filenameTemplates.map {
FilenameTemplateModel( FilenameTemplateModel(
it.isActive, it.isActive,
it.title,
it.name, it.name,
it.description, it.description,
it.template it.template

View File

@ -2,7 +2,6 @@ package de.idetools.filename_template.settings
data class FilenameTemplateModel( data class FilenameTemplateModel(
var isActive: Boolean = true, var isActive: Boolean = true,
var title: String = "",
var name: String = "", var name: String = "",
var description: String = "", var description: String = "",
var template: String = "" var template: String = ""

View File

@ -26,7 +26,7 @@ class FilenameTemplateSettingsComponent(val filenameTemplates: ArrayList<Filenam
activeColumn.resizable = false activeColumn.resizable = false
activeColumn.maxWidth = determineRequiredWidth(activeColumn) activeColumn.maxWidth = determineRequiredWidth(activeColumn)
val templateColumn = filenameTemplateTable.columnModel.getColumn(2) val templateColumn = filenameTemplateTable.columnModel.getColumn(3)
templateColumn.cellEditor = PlaceholderCompletionCellEditor() templateColumn.cellEditor = PlaceholderCompletionCellEditor()
val filenameTemplateTableScrollPane = JBScrollPane(filenameTemplateTable) val filenameTemplateTableScrollPane = JBScrollPane(filenameTemplateTable)

View File

@ -5,7 +5,7 @@ import javax.swing.table.AbstractTableModel
class FilenameTemplateTableModel(private val filenameTemplates: ArrayList<FilenameTemplateModel>) : class FilenameTemplateTableModel(private val filenameTemplates: ArrayList<FilenameTemplateModel>) :
AbstractTableModel() { AbstractTableModel() {
private val header: List<String> = listOf("Active", "Title", "Template") private val header: List<String> = listOf("Active", "Name", "Description", "Template")
override fun getRowCount(): Int = filenameTemplates.size override fun getRowCount(): Int = filenameTemplates.size
@ -14,8 +14,9 @@ class FilenameTemplateTableModel(private val filenameTemplates: ArrayList<Filena
override fun getValueAt(row: Int, column: Int): Any { override fun getValueAt(row: Int, column: Int): Any {
return when (column) { return when (column) {
0 -> filenameTemplates[row].isActive 0 -> filenameTemplates[row].isActive
1 -> filenameTemplates[row].title 1 -> filenameTemplates[row].name
2 -> filenameTemplates[row].template 2 -> filenameTemplates[row].description
3 -> filenameTemplates[row].template
else -> throw ArrayIndexOutOfBoundsException() else -> throw ArrayIndexOutOfBoundsException()
} }
} }
@ -23,8 +24,9 @@ class FilenameTemplateTableModel(private val filenameTemplates: ArrayList<Filena
override fun setValueAt(value: Any?, row: Int, column: Int) { override fun setValueAt(value: Any?, row: Int, column: Int) {
when (column) { when (column) {
0 -> filenameTemplates[row].isActive = value as Boolean 0 -> filenameTemplates[row].isActive = value as Boolean
1 -> filenameTemplates[row].title = value as String 1 -> filenameTemplates[row].name = value as String
2 -> filenameTemplates[row].template = value as String 2 -> filenameTemplates[row].description = value as String
3 -> filenameTemplates[row].template = value as String
else -> throw ArrayIndexOutOfBoundsException() else -> throw ArrayIndexOutOfBoundsException()
} }
} }

View File

@ -13,8 +13,7 @@
<ol> <ol>
<li>Settings -> Tools -> Filename Template</li> <li>Settings -> Tools -> Filename Template</li>
<li>Configure filename templates</li> <li>Configure filename templates</li>
<li>Create new file: New -> Filename Template</li> <li>Create new file: New -> <i>Template name</i></li>
<li>Choose filename template</li>
<li>Complete the file name with return</li> <li>Complete the file name with return</li>
</ol> </ol>
@ -22,10 +21,13 @@
<dl> <dl>
<dt>Active</dt> <dt>Active</dt>
<dd>Only active filename templates are available in the template selection list.</dd> <dd>Only active filename templates will be shown in the menu.</dd>
<dt>Title</dt> <dt>Name</dt>
<dd>The title is displayed in the template selection list below the file name input field.</dd> <dd>The name will be shown in the menu.</dd>
<dt>Description</dt>
<dd>The description will be shown in the status bar when hovering the menu entry.</dd>
<dt>Template</dt> <dt>Template</dt>
<dd> <dd>
@ -90,13 +92,11 @@
instance="de.idetools.filename_template.settings.FilenameTemplateConfigurable" instance="de.idetools.filename_template.settings.FilenameTemplateConfigurable"
id="de.idetools.filename_template.settings.FilenameTemplateConfigurable" id="de.idetools.filename_template.settings.FilenameTemplateConfigurable"
displayName="Filename Template"/> displayName="Filename Template"/>
<backgroundPostStartupActivity
implementation="de.idetools.filename_template.InitializeFilenameTemplateActivity"/>
</extensions> </extensions>
<actions> <actions>
<action id="de.idetools.filename_template.CreateFileWithFilenameTemplateAction" <!-- Add your actions here -->
class="de.idetools.filename_template.CreateFileWithFilenameTemplateAction" text="NewFilenameTemplate"
description="Create a new file based on a filename template" icon="/META-INF/pluginIcon-small.png">
<add-to-group group-id="NewGroup" anchor="after" relative-to-action="NewFile"/>
</action>
</actions> </actions>
</idea-plugin> </idea-plugin>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B