Compare commits

..

44 Commits
v1.0 ... master

Author SHA1 Message Date
49b94aae69 Make plugin compatible with idea version 2024.1 2024-04-04 07:14:25 +02:00
86c2723df2 Make plugin compatible with idea version 2023.3 2023-12-07 11:16:28 +01:00
060c21d9b9 Make plugin compatible with idea version 2023.2 2023-08-09 09:21:23 +02:00
d2f18c3bf5 Make plugin compatible with idea version 2023.1 2023-04-15 07:48:32 +02:00
bb6e5398a6 Make plugin compatible with idea version 2022.3 2023-03-18 06:21:10 +01:00
558e565606 Make plugin compatible with idea version 2022.2 2022-08-01 14:37:51 +02:00
ed602312de Make plugin compatible with idea version 2022.1 2022-04-14 00:46:15 +02:00
4fc3b1a11a Provide publish plugin token 2021-12-11 23:51:21 +01:00
7cec2bc5a7 Upgrade Java language level to 11 2021-12-11 23:33:18 +01:00
fa105a2919 Combine gradle project tasks 2021-12-11 23:17:20 +01:00
c31ea9a1fa Upgrade Gradle Wrapper to 7.3 2021-12-11 22:57:19 +01:00
f5f2317286 Suppress unused property inspection 2021-12-11 22:48:53 +01:00
c3b9203eab Use kotlin stdlib from platform 2021-12-11 22:48:26 +01:00
379cf0ee16 Get build properties from gradle.properties 2021-12-11 22:34:49 +01:00
54731f658e Upgrade gradle plugins 2021-12-11 22:14:57 +01:00
5e3a782ecb Make plugin compatible with idea version 2021.3 2021-12-11 05:47:31 +01:00
75cf64185a Make plugin compatible with idea version 2021.2 2021-08-07 09:48:04 +02:00
1d9fb818e4 Update version and documentation 2021-07-29 19:01:46 +02:00
7cfcad0c54 Combine name and description into title
With the templated new file dialog the description could not be shown anywhere. Simply deleting the description is not an option as some users might rely on this information.
2021-07-29 09:18:25 +02:00
d15d54e801 Provide placeholder completion cell editor 2021-07-29 08:23:34 +02:00
525b76b208 Disable action when no template is configured 2021-07-29 08:23:12 +02:00
eb94b71723 Reduce New menu actions to one
JetBrains plugins should only add one action to the New menu by using CreateWithTemplatesDialogPanel.
-- https://intellij-support.jetbrains.com/hc/en-us/community/posts/206755565-Override-New-menu-organisation?page=1#community_comment_206095325
2021-07-29 08:19:35 +02:00
183a44bc00 Minimize column "Active" width 2021-06-20 23:58:06 +02:00
7474792579 Initialize placeholders only at startup 2021-06-20 23:57:31 +02:00
cef38d89b1 Make new filename templates active by default 2021-06-20 23:48:28 +02:00
543435b7a0 Update version and release notes 2021-06-20 09:53:22 +02:00
e7f3c6f80e Add caret position placeholder 2021-06-20 09:45:50 +02:00
d72198746f Add sequence placeholder 2021-06-20 05:48:11 +02:00
72956bfd58 Make current filename accessible to placeholders 2021-06-20 05:48:11 +02:00
b8bea349ba Add username placeholder 2021-06-20 05:48:10 +02:00
869eb2cbc7 Add hostname placeholder 2021-06-20 05:48:02 +02:00
fc81383e1f Add project name placeholder 2021-06-20 05:47:57 +02:00
4b542a2677 Add formatted date placeholder 2021-06-20 05:47:52 +02:00
e5041d1822 Extract placeholder processing 2021-06-13 15:01:23 +02:00
fe1b4db39a Refactor placeholder processing
Iterate over the characters of the template string and process placeholders from left to right instead of replacing placeholders in an arbitrary order. The main benefit to this approach is avoiding regex to locate the placeholders. Finding the placeholders with regex is trickier than it looks like at first glance especially when nested placeholder boundary characters have to be considered. For example '{' and '}' are reserved date pattern characters which might be used within a date placeholder in the future.
2021-06-10 12:36:11 +02:00
b1f1575809 Make plugin compatible with idea version 2021.1 2021-06-10 08:35:36 +02:00
5b2648f0c9 Make plugin compatible with idea version 2020.3 2021-06-10 08:31:09 +02:00
674aaf9f04 Make plugin compatible with idea version 2020.2 2021-06-10 08:31:02 +02:00
ae55501210 Make plugin compatible with idea version 2020.1 2021-06-10 08:30:56 +02:00
32bf4dfacb Make plugin compatible with idea version 2019.3 2021-06-10 08:30:39 +02:00
6262bda2fb Make plugin compatible with idea version 2019.2 2021-06-10 08:30:31 +02:00
7ca666d59c Make plugin compatible with idea version 2019.1 2021-06-10 08:30:22 +02:00
3a11318bf7 Make plugin compatible with idea version 2018.3 2021-06-10 08:30:16 +02:00
fd9a45d49a Make plugin compatible with idea version 2018.2 2021-06-10 08:30:05 +02:00
35 changed files with 737 additions and 231 deletions

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" /> <option name="BUILD_PROCESS_HEAP_SIZE" value="2048" />
<bytecodeTargetLevel target="17" />
</component> </component>
</project> </project>

102
.idea/icon.svg Normal file
View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
version="1.1"
id="svg929"
sodipodi:docname="pluginIcon_dark.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata933">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1007"
id="namedview931"
showgrid="false"
inkscape:zoom="17.706014"
inkscape:cx="31.770979"
inkscape:cy="17.766763"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg929"
inkscape:pagecheckerboard="false" />
<defs>
<inkscape:path-effect
effect="powermask"
id="path-effect1102"
is_visible="true"
lpeversion="1"
uri="#mask-powermask-path-effect1102"
invert="false"
hide_mask="false"
background="true"
background_color="#ffffffff" />
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect1102">
<path
id="mask-powermask-path-effect1102_box"
style="fill:#ffffff;fill-opacity:1"
d="M 7,3 H 33 V 37 H 7 Z" />
<path
id="path1100"
style="opacity:1;fill:#aaaaaa;fill-opacity:1;stroke:#00a700;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 26.009922,4.0000005 32,10 H 27.763434 C 26.786492,10 26,9.2135087 26,8.2365661 Z"
sodipodi:nodetypes="ccscc" />
</mask>
<filter
id="mask-powermask-path-effect1102_inverse"
inkscape:label="filtermask-powermask-path-effect1102"
style="color-interpolation-filters:sRGB"
height="100"
width="100"
x="-50"
y="-50">
<feColorMatrix
id="mask-powermask-path-effect1102_primitive1"
values="1"
type="saturate"
result="fbSourceGraphic" />
<feColorMatrix
id="mask-powermask-path-effect1102_primitive2"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
in="fbSourceGraphic" />
</filter>
</defs>
<path
id="rect927"
mask="url(#mask-powermask-path-effect1102)"
style="fill:#b9d6f2;fill-opacity:1"
d="M 9.6210938 4 C 8.7230661 4 8 4.7230663 8 5.6210938 L 8 34.378906 C 8 35.276933 8.7230661 36 9.6210938 36 L 30.378906 36 C 31.276933 36 32 35.276933 32 34.378906 L 32 10 L 26.009766 4 L 9.6210938 4 z M 19.214844 12.783203 L 20.785156 12.783203 L 20.785156 27.216797 L 19.214844 27.216797 L 19.214844 12.783203 z M 16.373047 13.414062 L 16.373047 15.027344 C 15.991713 15.037124 15.68778 15.105313 15.462891 15.232422 C 15.238001 15.359533 15.126953 15.628395 15.126953 16.039062 L 15.126953 18.181641 C 15.126953 19.07142 14.567794 19.599402 13.453125 19.765625 L 13.453125 19.853516 C 14.567794 20.019738 15.126953 20.54772 15.126953 21.4375 L 15.126953 23.578125 C 15.126953 23.988793 15.238001 24.257654 15.462891 24.384766 C 15.68778 24.511877 15.991713 24.580066 16.373047 24.589844 L 16.373047 26.203125 C 15.541934 26.203125 14.897057 26.134936 14.4375 25.998047 C 13.987721 25.861158 13.674047 25.64685 13.498047 25.353516 C 13.322046 25.060182 13.234375 24.678319 13.234375 24.208984 L 13.234375 21.951172 C 13.234375 21.491615 13.078514 21.164306 12.765625 20.96875 C 12.452735 20.773194 11.996836 20.673828 11.400391 20.673828 L 11.400391 18.958984 C 11.996836 18.958984 12.452735 18.861572 12.765625 18.666016 C 13.078514 18.47046 13.234375 18.14315 13.234375 17.683594 L 13.234375 15.410156 C 13.234375 14.940822 13.322046 14.558959 13.498047 14.265625 C 13.674047 13.972291 13.987721 13.75603 14.4375 13.619141 C 14.897057 13.482251 15.541934 13.414062 16.373047 13.414062 z M 23.626953 13.414062 C 24.467844 13.414062 25.112721 13.482251 25.5625 13.619141 C 26.012279 13.75603 26.325953 13.972291 26.501953 14.265625 C 26.677954 14.558959 26.765625 14.940822 26.765625 15.410156 L 26.765625 17.683594 C 26.765625 18.14315 26.921486 18.47046 27.234375 18.666016 C 27.547265 18.861572 28.003164 18.958984 28.599609 18.958984 L 28.599609 20.673828 C 28.003164 20.673828 27.547265 20.773194 27.234375 20.96875 C 26.921486 21.164306 26.765625 21.491615 26.765625 21.951172 L 26.765625 24.208984 C 26.765625 24.678319 26.677954 25.060182 26.501953 25.353516 C 26.325953 25.64685 26.012279 25.861158 25.5625 25.998047 C 25.112721 26.134936 24.467844 26.203125 23.626953 26.203125 L 23.626953 24.589844 C 24.008287 24.580064 24.31222 24.511875 24.537109 24.384766 C 24.761999 24.257654 24.873047 23.988793 24.873047 23.578125 L 24.873047 21.4375 C 24.873047 20.54772 25.432206 20.019738 26.546875 19.853516 L 26.546875 19.765625 C 25.432206 19.599402 24.873047 19.07142 24.873047 18.181641 L 24.873047 16.039062 C 24.873047 15.628395 24.761999 15.359533 24.537109 15.232422 C 24.31222 15.105311 24.008287 15.037122 23.626953 15.027344 L 23.626953 13.414062 z " />
<g
aria-label="{|}"
id="text980"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:14.6667px;line-height:1.25;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans Bold';letter-spacing:-1px;fill:#000000;fill-opacity:1;stroke:none"
transform="translate(-2.15e-5,1.6039835)" />
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

6
.idea/kotlinc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.21" />
</component>
</project>

View File

@ -4,7 +4,7 @@
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" /> <file type="web" url="file://$PROJECT_DIR$" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_14" default="true" project-jdk-name="openjdk-14" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="openjdk-19" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/filename_template-merge.iml" filepath="$PROJECT_DIR$/.idea/filename_template-merge.iml" />
</modules>
</component>
</project>

View File

@ -1,38 +1,71 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
fun properties(key: String) = project.findProperty(key).toString()
plugins { plugins {
id("org.jetbrains.intellij") version "0.6.5" id("org.jetbrains.intellij") version "1.17.3"
kotlin("jvm") version "1.4.10" id("org.jetbrains.kotlin.jvm") version "1.9.21"
} }
val pluginVersion: String by project group = properties("pluginGroup")
val platformVersion: String by project version = properties("pluginVersion")
group = "de.idetools"
version = pluginVersion
repositories { repositories {
mavenCentral() mavenCentral()
} }
dependencies {
implementation(kotlin("stdlib"))
}
// See https://github.com/JetBrains/gradle-intellij-plugin/ // See https://github.com/JetBrains/gradle-intellij-plugin/
intellij { intellij {
version = platformVersion pluginName.set(properties("pluginName"))
version.set(properties("platformVersion"))
} }
tasks.getByName<org.jetbrains.intellij.tasks.PatchPluginXmlTask>("patchPluginXml") {
changeNotes(""" tasks {
// Set the JVM compatibility versions
properties("javaVersion").let {
withType<JavaCompile> {
sourceCompatibility = it
targetCompatibility = it
}
withType<KotlinCompile> {
kotlinOptions {
jvmTarget = it
}
}
}
wrapper {
gradleVersion = properties("gradleVersion")
}
patchPluginXml {
changeNotes.set(
"""
<h3>[1.2]</h3>
<ul>
<li>Single New menu action to access filename template configurations</li>
<li>Simplify template configuration with placeholder auto completion</li>
<li>New templates are active by default</li>
<li>Combine name and description into title</li>
</ul>
<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>
<li>Initial release</li> <li>Initial release</li>
</ul> </ul>
""") """
} )
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { publishPlugin {
kotlinOptions { token.set(System.getenv("PUBLISH_TOKEN"))
jvmTarget = "1.8"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,4 +1,18 @@
# suppress inspection "UnusedProperty"
kotlin.code.style=official kotlin.code.style=official
pluginVersion = 1.0.0-181 pluginGroup = de.idetools
platformVersion = 2018.1 pluginName = filename_template
pluginVersion = 1.2.0-241
platformVersion = 2024.1
# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3
javaVersion = 17
org.gradle.jvmargs=-Xmx2048m
gradleVersion = 8.2.1
# Opt-out flag for bundling Kotlin standard library.
# See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details.
# suppress inspection "UnusedProperty"
kotlin.stdlib.default.dependency = false

Binary file not shown.

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

269
gradlew vendored
View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015-2021 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -17,67 +17,101 @@
# #
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
@ -106,80 +140,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=`save "$@"` # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

View File

@ -3,35 +3,117 @@ 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.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.InputValidatorEx
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 java.time.LocalDateTime import de.idetools.filename_template.placeholders.*
import java.time.ZoneOffset import de.idetools.filename_template.settings.FilenameTemplateSettingsState
import java.awt.event.InputEvent
import java.util.function.Consumer
import java.util.function.Supplier
import kotlin.math.max
class CreateFileWithFilenameTemplateAction( class CreateFileWithFilenameTemplateAction :
text: String, CreateFileAction(Supplier { "Filename Template"}, Supplier { "Create a new file based on template"}, Supplier { AllIcons.Actions.AddFile }) {
description: String,
private val template: String
) :
CreateFileAction(text, description, AllIcons.FileTypes.Unknown) {
override fun invokeDialog(project: Project?, directory: PsiDirectory?): Array<PsiElement> { private val placeholders: List<Placeholder> = listOf(
val validator: MyInputValidator = MyValidator(project, directory) EpochPlaceholder(),
Messages.showInputDialog( DatePlaceholder(),
project, ProjectPlaceholder(),
IdeBundle.message("prompt.enter.new.file.name"), SequencePlaceholder(),
IdeBundle.message("title.new.file"), HostPlaceholder(),
null, UserPlaceholder(),
processTemplate(template), DefaultPlaceholder()
validator
) )
return validator.createdElements
override fun update(e: AnActionEvent) {
if (FilenameTemplateSettingsState.instance.filenameTemplates.isEmpty()) {
e.presentation.isEnabledAndVisible = false
} else {
super.update(e)
}
} }
private fun processTemplate(template: String): String { override fun invokeDialog(
val epoch = LocalDateTime.now(ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC) project: Project,
return template.replace("{EPOCH}", epoch.toString()) directory: PsiDirectory,
elementsConsumer: Consumer<in 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 contentPanel = CreateWithFilenameTemplateDialogPanel(templates)
val nameField = contentPanel.textField
val popup = NewItemPopupUtil.createNewItemPopup(IdeBundle.message("title.new.file"), contentPanel, nameField)
contentPanel.setApplyAction { event: InputEvent? ->
val name = nameField.text
if (validator.checkInput(name) && validator.canClose(name)) {
popup.closeOk(event)
elementsConsumer.accept(validator.createdElements)
} else {
val errorMessage = (validator as InputValidatorEx).getErrorText(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

@ -0,0 +1,34 @@
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.first().first, templates.map {
trinity -> TemplatePresentation(trinity.first, trinity.second, trinity.third)
}) {
init {
updateSelectedText()
myTemplatesList.addListSelectionListener { updateSelectedText() }
}
private fun updateSelectedText() {
val selectedValue = myTemplatesList.selectedValue
if (selectedValue != null) {
var filename = selectedValue.templateName
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

@ -1,40 +0,0 @@
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.settings.FilenameTemplateSettingsState
class InitializeFilenameTemplateActivity : StartupActivity {
override fun runActivity(project: Project) {
initializeActions()
}
internal fun initializeActions() {
val settings: FilenameTemplateSettingsState = FilenameTemplateSettingsState.instance
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,
it.template
)
val actionId = "FilenameTemplate.${it.name}"
actionManager.registerAction(actionId, action)
newGroup.addAction(action, Constraints(Anchor.AFTER, anchorId))
anchorId = actionId
}
}
}

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,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

@ -2,7 +2,6 @@ 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
@ -12,8 +11,16 @@ 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.name, it.description, it.template) FilenameTemplateModel(it.isActive, it.title, it.name, it.description, it.template)
})) }))
} }
@ -32,9 +39,8 @@ 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.name || component.filenameTemplateTable.getValueAt(it.index, 1) != it.value.title
|| component.filenameTemplateTable.getValueAt(it.index, 2) != it.value.description || component.filenameTemplateTable.getValueAt(it.index, 2) != it.value.template
|| component.filenameTemplateTable.getValueAt(it.index, 3) != it.value.template
} }
} }
@ -45,12 +51,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() {
@ -59,6 +65,7 @@ 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

@ -1,7 +1,8 @@
package de.idetools.filename_template.settings package de.idetools.filename_template.settings
data class FilenameTemplateModel( data class FilenameTemplateModel(
var isActive: Boolean = false, 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

@ -10,6 +10,7 @@ import com.intellij.ui.table.JBTable
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.FlowLayout import java.awt.FlowLayout
import javax.swing.JPanel import javax.swing.JPanel
import javax.swing.table.TableColumn
class FilenameTemplateSettingsComponent(val filenameTemplates: ArrayList<FilenameTemplateModel>) { class FilenameTemplateSettingsComponent(val filenameTemplates: ArrayList<FilenameTemplateModel>) {
private var filenameTemplateTableModel: FilenameTemplateTableModel = FilenameTemplateTableModel(filenameTemplates) private var filenameTemplateTableModel: FilenameTemplateTableModel = FilenameTemplateTableModel(filenameTemplates)
@ -21,12 +22,29 @@ class FilenameTemplateSettingsComponent(val filenameTemplates: ArrayList<Filenam
filenameTemplateTable = JBTable(filenameTemplateTableModel) filenameTemplateTable = JBTable(filenameTemplateTableModel)
filenameTemplateTable.autoCreateRowSorter = false filenameTemplateTable.autoCreateRowSorter = false
val activeColumn = filenameTemplateTable.columnModel.getColumn(0)
activeColumn.resizable = false
activeColumn.maxWidth = determineRequiredWidth(activeColumn)
val templateColumn = filenameTemplateTable.columnModel.getColumn(2)
templateColumn.cellEditor = PlaceholderCompletionCellEditor()
val filenameTemplateTableScrollPane = JBScrollPane(filenameTemplateTable) val filenameTemplateTableScrollPane = JBScrollPane(filenameTemplateTable)
panel.add(filenameTemplateTableScrollPane, BorderLayout.CENTER) panel.add(filenameTemplateTableScrollPane, BorderLayout.CENTER)
panel.add(createToolbar(filenameTemplateTable), BorderLayout.EAST) panel.add(createToolbar(filenameTemplateTable), BorderLayout.EAST)
} }
private fun determineRequiredWidth(column: TableColumn): Int =
filenameTemplateTable.tableHeader.defaultRenderer.getTableCellRendererComponent(
filenameTemplateTable,
column.headerValue,
false,
false,
0,
0
).preferredSize.width + 8
private fun createToolbar(table: JBTable): JPanel { private fun createToolbar(table: JBTable): JPanel {
val actionGroup = DefaultActionGroup() val actionGroup = DefaultActionGroup()
actionGroup.add(object : AnAction("Add", "Add new filename template", AllIcons.General.Add) { actionGroup.add(object : AnAction("Add", "Add new filename template", AllIcons.General.Add) {

View File

@ -1,7 +1,7 @@
package de.idetools.filename_template.settings package de.idetools.filename_template.settings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.components.State import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage import com.intellij.openapi.components.Storage
import com.intellij.util.xmlb.XmlSerializerUtil import com.intellij.util.xmlb.XmlSerializerUtil
@ -23,6 +23,6 @@ class FilenameTemplateSettingsState : PersistentStateComponent<FilenameTemplateS
companion object { companion object {
val instance: FilenameTemplateSettingsState val instance: FilenameTemplateSettingsState
get() = ServiceManager.getService(FilenameTemplateSettingsState::class.java) get() = ApplicationManager.getApplication().getService(FilenameTemplateSettingsState::class.java)
} }
} }

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", "Name", "Description", "Template") private val header: List<String> = listOf("Active", "Title", "Template")
override fun getRowCount(): Int = filenameTemplates.size override fun getRowCount(): Int = filenameTemplates.size
@ -14,9 +14,8 @@ 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].name 1 -> filenameTemplates[row].title
2 -> filenameTemplates[row].description 2 -> filenameTemplates[row].template
3 -> filenameTemplates[row].template
else -> throw ArrayIndexOutOfBoundsException() else -> throw ArrayIndexOutOfBoundsException()
} }
} }
@ -24,9 +23,8 @@ 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].name = value as String 1 -> filenameTemplates[row].title = value as String
2 -> filenameTemplates[row].description = value as String 2 -> filenameTemplates[row].template = value as String
3 -> filenameTemplates[row].template = value as String
else -> throw ArrayIndexOutOfBoundsException() else -> throw ArrayIndexOutOfBoundsException()
} }
} }

View File

@ -0,0 +1,68 @@
package de.idetools.filename_template.settings
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.project.ProjectManager
import com.intellij.util.TextFieldCompletionProviderDumbAware
import com.intellij.util.textCompletion.TextFieldWithCompletion
import java.awt.Component
import javax.swing.AbstractCellEditor
import javax.swing.JTable
import javax.swing.table.TableCellEditor
internal class PlaceholderCompletionCellEditor : AbstractCellEditor(), TableCellEditor {
private var component = TextFieldWithCompletion(
ProjectManager.getInstance().defaultProject,
PlaceholderCompletionProvider(),
"",
true,
true,
true
)
override fun getCellEditorValue(): Any {
return component.text
}
override fun getTableCellEditorComponent(
table: JTable,
value: Any,
isSelected: Boolean,
rowIndex: Int,
vColIndex: Int
): Component {
component.text = value as String
return component
}
class PlaceholderCompletionProvider : TextFieldCompletionProviderDumbAware(true) {
object PlaceholderInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, lookupElement: LookupElement) {
val text = context.document.text
if (context.tailOffset < text.length && text[context.tailOffset] == '}') {
context.document.replaceString(context.tailOffset - 1, context.tailOffset + 1, "}")
}
if (0 < context.startOffset && text[context.startOffset - 1] == '{') {
context.document.replaceString(context.startOffset - 1, context.startOffset + 1, "{")
}
}
}
override fun getPrefix(currentTextPrefix: String): String {
if (-1 < currentTextPrefix.indexOf('{')) {
return currentTextPrefix.removeRange(0, currentTextPrefix.lastIndexOf('{'))
}
return currentTextPrefix
}
override fun addCompletionVariants(text: String, offset: Int, prefix: String, result: CompletionResultSet) {
listOf("|", "0", "DATE", "EPOCH", "HOST", "PROJECT", "USER").forEach {
result.addElement(LookupElementBuilder.create("{${it}}").withInsertHandler(PlaceholderInsertHandler))
}
}
}
}

View File

@ -13,7 +13,8 @@
<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 -> <i>Template name</i></li> <li>Create new file: New -> Filename Template</li>
<li>Choose filename template</li>
<li>Complete the file name with return</li> <li>Complete the file name with return</li>
</ol> </ol>
@ -21,13 +22,10 @@
<dl> <dl>
<dt>Active</dt> <dt>Active</dt>
<dd>Only active filename templates will be shown in the menu.</dd> <dd>Only active filename templates are available in the template selection list.</dd>
<dt>Name</dt> <dt>Title</dt>
<dd>The name will be shown in the menu.</dd> <dd>The title is displayed in the template selection list below the file name input field.</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>
@ -35,8 +33,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>
@ -62,11 +90,13 @@
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"/>
<postStartupActivity
implementation="de.idetools.filename_template.InitializeFilenameTemplateActivity"/>
</extensions> </extensions>
<actions> <actions>
<!-- Add your actions here --> <action id="de.idetools.filename_template.CreateFileWithFilenameTemplateAction"
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.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B