Merge branch 'idea-182' into idea-181

This commit is contained in:
Matthias Gugel 2021-06-20 23:41:10 +02:00
commit 2c228852b4
16 changed files with 227 additions and 12 deletions

View File

@ -23,6 +23,12 @@ intellij {
} }
tasks.getByName<org.jetbrains.intellij.tasks.PatchPluginXmlTask>("patchPluginXml") { tasks.getByName<org.jetbrains.intellij.tasks.PatchPluginXmlTask>("patchPluginXml") {
changeNotes(""" changeNotes("""
<h3>[1.1]</h3>
<ul>
<li>Placeholders for: date, hostname, project name, username, sequential numbers, and caret position</li>
</ul>
<h3>[1.0]</h3> <h3>[1.0]</h3>
<ul> <ul>

View File

@ -1,4 +1,4 @@
kotlin.code.style=official kotlin.code.style=official
pluginVersion = 1.0.0-181 pluginVersion = 1.1.0-181
platformVersion = 2018.1 platformVersion = 2018.1

View File

@ -7,31 +7,26 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import java.time.LocalDateTime import de.idetools.filename_template.placeholders.PlaceholderProcessor
import java.time.ZoneOffset
class CreateFileWithFilenameTemplateAction( class CreateFileWithFilenameTemplateAction(
text: String, text: String,
description: String, description: String,
private val template: String private val placeholderProcessor: PlaceholderProcessor
) : ) :
CreateFileAction(text, description, AllIcons.FileTypes.Unknown) { CreateFileAction(text, description, AllIcons.FileTypes.Unknown) {
override fun invokeDialog(project: Project?, directory: PsiDirectory?): Array<PsiElement> { override fun invokeDialog(project: Project, directory: PsiDirectory): Array<PsiElement> {
val validator: MyInputValidator = MyValidator(project, directory) val validator: MyInputValidator = MyValidator(project, directory)
val processedTemplate = placeholderProcessor.processTemplate(project, directory)
Messages.showInputDialog( Messages.showInputDialog(
project, project,
IdeBundle.message("prompt.enter.new.file.name"), IdeBundle.message("prompt.enter.new.file.name"),
IdeBundle.message("title.new.file"), IdeBundle.message("title.new.file"),
null, null,
processTemplate(template), processedTemplate.filename,
validator validator
) )
return validator.createdElements return validator.createdElements
} }
private fun processTemplate(template: String): String {
val epoch = LocalDateTime.now(ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC)
return template.replace("{EPOCH}", epoch.toString())
}
} }

View File

@ -6,6 +6,14 @@ import com.intellij.openapi.actionSystem.Constraints
import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity 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 import de.idetools.filename_template.settings.FilenameTemplateSettingsState
class InitializeFilenameTemplateActivity : StartupActivity { class InitializeFilenameTemplateActivity : StartupActivity {
@ -16,6 +24,16 @@ class InitializeFilenameTemplateActivity : StartupActivity {
internal fun initializeActions() { internal fun initializeActions() {
val settings: FilenameTemplateSettingsState = FilenameTemplateSettingsState.instance val settings: FilenameTemplateSettingsState = FilenameTemplateSettingsState.instance
val placeholders = listOf(
EpochPlaceholder(),
DatePlaceholder(),
ProjectPlaceholder(),
HostPlaceholder(),
SequencePlaceholder(),
UserPlaceholder(),
DefaultPlaceholder()
)
val actionManager = ActionManager.getInstance() val actionManager = ActionManager.getInstance()
val newGroup = actionManager.getAction("NewGroup") as DefaultActionGroup val newGroup = actionManager.getAction("NewGroup") as DefaultActionGroup
@ -29,7 +47,7 @@ class InitializeFilenameTemplateActivity : StartupActivity {
val action = CreateFileWithFilenameTemplateAction( val action = CreateFileWithFilenameTemplateAction(
it.name, it.name,
it.description, it.description,
it.template PlaceholderProcessor(it.template, placeholders)
) )
val actionId = "FilenameTemplate.${it.name}" val actionId = "FilenameTemplate.${it.name}"
actionManager.registerAction(actionId, action) actionManager.registerAction(actionId, action)

View File

@ -0,0 +1,20 @@
package de.idetools.filename_template.placeholders
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class DatePlaceholder : Placeholder {
override fun supports(placeholder: String): Boolean = placeholder.startsWith("DATE")
override fun process(placeholder: String, context: PlaceholderContext): String {
val date = LocalDateTime.now()
val datePattern = placeholder.substringAfter(":", "")
val formatter =
if (datePattern.isBlank()) {
DateTimeFormatter.ISO_DATE
} else {
DateTimeFormatter.ofPattern(datePattern)
}
return date.format(formatter)
}
}

View File

@ -0,0 +1,8 @@
package de.idetools.filename_template.placeholders
class DefaultPlaceholder : Placeholder {
override fun supports(placeholder: String): Boolean = true
override fun process(placeholder: String, context: PlaceholderContext): String =
"{$placeholder}"
}

View File

@ -0,0 +1,11 @@
package de.idetools.filename_template.placeholders
import java.time.LocalDateTime
import java.time.ZoneOffset
class EpochPlaceholder : Placeholder {
override fun supports(placeholder: String): Boolean = placeholder == "EPOCH"
override fun process(placeholder: String, context: PlaceholderContext): String =
LocalDateTime.now(ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC).toString()
}

View File

@ -0,0 +1,10 @@
package de.idetools.filename_template.placeholders
import java.net.InetAddress
class HostPlaceholder : Placeholder {
override fun supports(placeholder: String): Boolean = placeholder == "HOST"
override fun process(placeholder: String, context: PlaceholderContext): String =
InetAddress.getLocalHost().hostName
}

View File

@ -0,0 +1,6 @@
package de.idetools.filename_template.placeholders
interface Placeholder {
fun supports(placeholder: String): Boolean
fun process(placeholder: String, context: PlaceholderContext): String
}

View File

@ -0,0 +1,10 @@
package de.idetools.filename_template.placeholders
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDirectory
data class PlaceholderContext(
val project: Project,
val directory: PsiDirectory,
var filename: String = ""
)

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

@ -0,0 +1,6 @@
package de.idetools.filename_template.placeholders
data class ProcessedTemplate(
val filename: String,
val caretPosition: Int
)

View File

@ -0,0 +1,8 @@
package de.idetools.filename_template.placeholders
class ProjectPlaceholder : Placeholder {
override fun supports(placeholder: String): Boolean = placeholder == "PROJECT"
override fun process(placeholder: String, context: PlaceholderContext): String =
context.project.name
}

View File

@ -0,0 +1,22 @@
package de.idetools.filename_template.placeholders
import com.intellij.psi.PsiFile
class SequencePlaceholder : Placeholder {
override fun supports(placeholder: String): Boolean = placeholder.matches("^\\d+$".toRegex())
override fun process(placeholder: String, context: PlaceholderContext): String {
var value = Integer.parseInt(placeholder)
while (sequenceValueExists(context.directory.files, context.filename + formatSequence(value, placeholder.length))) {
value++
}
return formatSequence(value, placeholder.length)
}
private fun sequenceValueExists(files: Array<PsiFile>, sequence: String): Boolean {
return files.any { it.name.startsWith(sequence) }
}
private fun formatSequence(value: Int, length: Int) =
value.toString().padStart(length, '0')
}

View File

@ -0,0 +1,8 @@
package de.idetools.filename_template.placeholders
class UserPlaceholder : Placeholder {
override fun supports(placeholder: String): Boolean = placeholder == "USER"
override fun process(placeholder: String, context: PlaceholderContext): String =
System.getProperty("user.name")
}

View File

@ -35,8 +35,38 @@
<br/> <br/>
<p>A template might contain following place holders:</p> <p>A template might contain following place holders:</p>
<dl> <dl>
<dt>{DATE:FORMAT}</dt>
<dd>
The formatted date. If the format is omitted "yyyy-MM-dd" is used by default.<br />
See the java documentation for supported patterns: <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/format/DateTimeFormatter.html">Patterns for Formatting and Parsing</a>
</dd>
<dt>{EPOCH}</dt> <dt>{EPOCH}</dt>
<dd>The current unix time stamp: Seconds since the Unix Epoch on January 1st, 1970 at UTC.</dd> <dd>The current unix time stamp: Seconds since the Unix Epoch on January 1st, 1970 at UTC.</dd>
<dt>{HOST}</dt>
<dd>
The computers hostname.
</dd>
<dt>{PROJECT}</dt>
<dd>
The name of the current project.
</dd>
<dt>{USER}</dt>
<dd>
The username of the user running the IDE.
</dd>
<dt>{###}</dt>
<dd>
The first available sequential number. Define the initial value and desired leading zeros in the template.
Example: notes({042}).md => notes(042).md, notes(043).md, ...
</dd>
<dt>{|}</dt>
<dd>The caret position. If omitted the caret is positioned before the first '.' if available or at the end.</dd>
</dl> </dl>
</dd> </dd>
</dl> </dl>