@@ -24,7 +24,7 @@ import com.blankj.utilcode.util.FileIOUtils
2424import com.itsaky.androidide.actions.ActionData
2525import com.itsaky.androidide.actions.requireFile
2626import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder
27- import com.itsaky.androidide.databinding.LayoutCreateFileJavaBinding
27+ import com.itsaky.androidide.databinding.LayoutCreateFileClassBinding
2828import com.itsaky.androidide.eventbus.events.file.FileCreationEvent
2929import com.itsaky.androidide.preferences.databinding.LayoutDialogTextInputBinding
3030import com.itsaky.androidide.projects.IProjectManager
@@ -36,12 +36,12 @@ import com.itsaky.androidide.utils.SingleTextWatcher
3636import com.itsaky.androidide.utils.flashError
3737import com.itsaky.androidide.utils.flashSuccess
3838import com.unnamed.b.atv.model.TreeNode
39- import jdkx.lang.model.SourceVersion
40- import org.greenrobot.eventbus.EventBus
41- import org.slf4j.LoggerFactory
4239import java.io.File
4340import java.util.Objects
4441import java.util.regex.Pattern
42+ import jdkx.lang.model.SourceVersion
43+ import org.greenrobot.eventbus.EventBus
44+ import org.slf4j.LoggerFactory
4545
4646/* *
4747 * File tree action to create a new file.
@@ -52,7 +52,7 @@ class NewFileAction(context: Context, override val order: Int) :
5252 BaseDirNodeAction (
5353 context = context,
5454 labelRes = R .string.new_file,
55- iconRes = R .drawable.ic_new_file
55+ iconRes = R .drawable.ic_new_file,
5656 ) {
5757
5858 override val id: String = " ide.editor.fileTree.newFile"
@@ -63,7 +63,7 @@ class NewFileAction(context: Context, override val order: Int) :
6363 const val LAYOUT_RES_PATH_REGEX = " /.*/src/.*/res/layout"
6464 const val MENU_RES_PATH_REGEX = " /.*/src/.*/res/menu"
6565 const val DRAWABLE_RES_PATH_REGEX = " /.*/src/.*/res/drawable"
66- const val JAVA_PATH_REGEX = " /.*/src/.*/java"
66+ const val JAVA_OR_KOTLIN_PATH_REGEX = " /.*/src/.*/( java|kotlin) "
6767
6868 private val log = LoggerFactory .getLogger(NewFileAction ::class .java)
6969 }
@@ -84,7 +84,7 @@ class NewFileAction(context: Context, override val order: Int) :
8484 context : Context ,
8585 node : TreeNode ? ,
8686 file : File ,
87- forceUnknownType : Boolean
87+ forceUnknownType : Boolean ,
8888 ) {
8989 if (forceUnknownType) {
9090 createNewEmptyFile(context, node, file)
@@ -93,10 +93,14 @@ class NewFileAction(context: Context, override val order: Int) :
9393
9494 val projectDir = IProjectManager .getInstance().projectDirPath
9595 Objects .requireNonNull(projectDir)
96- val isJava =
97- Pattern .compile(Pattern .quote(projectDir) + JAVA_PATH_REGEX ).matcher(file.absolutePath).find()
96+ val isJavaOrKotlin =
97+ Pattern .compile(Pattern .quote(projectDir) + JAVA_OR_KOTLIN_PATH_REGEX )
98+ .matcher(file.absolutePath)
99+ .find()
98100 val isRes =
99- Pattern .compile(Pattern .quote(projectDir) + RES_PATH_REGEX ).matcher(file.absolutePath).find()
101+ Pattern .compile(Pattern .quote(projectDir) + RES_PATH_REGEX )
102+ .matcher(file.absolutePath)
103+ .find()
100104 val isLayoutRes =
101105 Pattern .compile(Pattern .quote(projectDir) + LAYOUT_RES_PATH_REGEX )
102106 .matcher(file.absolutePath)
@@ -110,8 +114,8 @@ class NewFileAction(context: Context, override val order: Int) :
110114 .matcher(file.absolutePath)
111115 .find()
112116
113- if (isJava ) {
114- createJavaClass (context, node, file)
117+ if (isJavaOrKotlin ) {
118+ createClass (context, node, file)
115119 return
116120 }
117121
@@ -138,16 +142,22 @@ class NewFileAction(context: Context, override val order: Int) :
138142 createNewEmptyFile(context, node, file)
139143 }
140144
141- private fun createJavaClass (context : Context , node : TreeNode ? , file : File ) {
145+ private fun createClass (context : Context , node : TreeNode ? , file : File ) {
142146 val builder = DialogUtils .newMaterialDialogBuilder(context)
143- val binding: LayoutCreateFileJavaBinding =
144- LayoutCreateFileJavaBinding .inflate(LayoutInflater .from(context))
145- binding.typeGroup.addOnButtonCheckedListener { _, _, _ ->
146- binding.createLayout.isVisible = binding.typeGroup.checkedButtonId == binding.typeActivity.id
147+ val binding: LayoutCreateFileClassBinding =
148+ LayoutCreateFileClassBinding .inflate(LayoutInflater .from(context))
149+ binding.classTypeGroup.addOnButtonCheckedListener { _, _, _ ->
150+ binding.createLayout.isVisible =
151+ binding.classTypeGroup.checkedButtonId == binding.typeActivity.id
147152 }
148153 binding.name.editText?.addTextChangedListener(
149154 object : SingleTextWatcher () {
150- override fun onTextChanged (s : CharSequence? , start : Int , before : Int , count : Int ) {
155+ override fun onTextChanged (
156+ s : CharSequence? ,
157+ start : Int ,
158+ before : Int ,
159+ count : Int ,
160+ ) {
151161 if (isValidJavaName(s)) {
152162 binding.name.isErrorEnabled = true
153163 binding.name.error = context.getString(R .string.msg_invalid_name)
@@ -158,7 +168,7 @@ class NewFileAction(context: Context, override val order: Int) :
158168 }
159169 )
160170 builder.setView(binding.root)
161- builder.setTitle(R .string.new_java_class )
171+ builder.setTitle(R .string.new_class )
162172 builder.setPositiveButton(R .string.text_create) { dialogInterface, _ ->
163173 dialogInterface.dismiss()
164174 try {
@@ -173,11 +183,11 @@ class NewFileAction(context: Context, override val order: Int) :
173183 builder.create().show()
174184 }
175185
176- private fun doCreateJavaFile (
177- binding : LayoutCreateFileJavaBinding ,
186+ private fun doCreateClassFile (
187+ binding : LayoutCreateFileClassBinding ,
178188 file : File ,
179189 context : Context ,
180- node : TreeNode ?
190+ node : TreeNode ? ,
181191 ) {
182192 if (binding.name.isErrorEnabled) {
183193 flashError(R .string.msg_invalid_name)
@@ -191,64 +201,89 @@ class NewFileAction(context: Context, override val order: Int) :
191201 }
192202
193203 val autoLayout =
194- binding.typeGroup .checkedButtonId == binding.typeActivity.id &&
195- binding.createLayout.isChecked
204+ binding.classTypeGroup .checkedButtonId == binding.typeActivity.id &&
205+ binding.createLayout.isChecked
196206 val pkgName = ProjectWriter .getPackageName(file)
197207 if (pkgName == null || pkgName.trim { it <= ' ' }.isEmpty()) {
198208 flashError(R .string.msg_get_package_failed)
199209 return
200210 }
201211
202- val id : Int = binding.typeGroup .checkedButtonId
203- val javaName = if (name.endsWith( " .java " )) name else " $name .java "
204- val className = if ( ! name.contains( " . " )) name else name.substring( 0 , name.lastIndexOf( " . " ))
212+ val fileTypeId : Int = binding.fileTypeGroup .checkedButtonId
213+ val classTypeId : Int = binding.classTypeGroup.checkedButtonId
214+
205215 val created =
206- when (id) {
207- binding.typeClass.id ->
208- createFile(
209- context,
210- node,
211- file,
212- javaName,
213- ProjectWriter .createJavaClass(pkgName, className)
214- )
215-
216- binding.typeInterface.id ->
217- createFile(
218- context,
219- node,
220- file,
221- javaName,
222- ProjectWriter .createJavaInterface(pkgName, className)
223- )
224-
225- binding.typeEnum.id ->
226- createFile(
227- context,
228- node,
229- file,
230- javaName,
231- ProjectWriter .createJavaEnum(pkgName, className)
232- )
233-
234- binding.typeActivity.id ->
235- createFile(
236- context,
237- node,
238- file,
239- javaName,
240- ProjectWriter .createActivity(pkgName, className)
241- )
242-
243- else -> createFile(context, node, file, name, " " )
244- }
216+ doCreateClassFile(
217+ fileTypeId == binding.typeKotlin.id,
218+ classTypeId,
219+ name,
220+ pkgName,
221+ node,
222+ )
245223
246224 if (created && autoLayout) {
247225 val packagePath = pkgName.toString().replace(" ." , " /" )
248226 createAutoLayout(context, file, name, packagePath)
249227 }
250228 }
251229
230+ private fun doCreateClassFile (
231+ isKotlin : Boolean ,
232+ classTypeId : Int ,
233+ name : String ,
234+ pkgName : String ,
235+ node : TreeNode ? ,
236+ ): Boolean {
237+ val fileName =
238+ when {
239+ isKotlin -> if (name.endsWith(" .kt" )) name else " $name .kt"
240+ else -> if (name.endsWith(" .java" )) name else " $name .java"
241+ }
242+ val className =
243+ if (! name.contains(" ." )) name
244+ else name.substring(0 , name.lastIndexOf(" ." ))
245+
246+ return when (classTypeId) {
247+ binding.typeClass.id ->
248+ createFile(
249+ context,
250+ node,
251+ file,
252+ fileName,
253+ ProjectWriter .createClass(isKotlin, pkgName, className),
254+ )
255+
256+ binding.typeInterface.id ->
257+ createFile(
258+ context,
259+ node,
260+ file,
261+ fileName,
262+ ProjectWriter .createInterface(isKotlin, pkgName, className),
263+ )
264+
265+ binding.typeEnum.id ->
266+ createFile(
267+ context,
268+ node,
269+ file,
270+ fileName,
271+ ProjectWriter .createEnum(isKotlin, pkgName, className),
272+ )
273+
274+ binding.typeActivity.id ->
275+ createFile(
276+ context,
277+ node,
278+ file,
279+ fileName,
280+ ProjectWriter .createActivity(isKotlin, pkgName, className),
281+ )
282+
283+ else -> createFile(context, node, file, name, " " )
284+ }
285+ }
286+
252287 private fun isValidJavaName (s : CharSequence? ) =
253288 s == null || ! SourceVersion .isName(s) || SourceVersion .isKeyword(s)
254289
@@ -258,25 +293,31 @@ class NewFileAction(context: Context, override val order: Int) :
258293 node,
259294 Environment .mkdirIfNotExits(file),
260295 ProjectWriter .createLayout(),
261- " .xml"
296+ " .xml" ,
262297 )
263298 }
264299
265300 private fun createAutoLayout (
266301 context : Context ,
267302 directory : File ,
268303 fileName : String ,
269- packagePath : String
304+ packagePath : String ,
270305 ) {
271306 val dir = directory.toString().replace(" java/$packagePath " , " res/layout/" )
272- val layoutName = ProjectWriter .createLayoutName(fileName.replace(" .java" , " .xml" ))
307+ val layoutName =
308+ ProjectWriter .createLayoutName(fileName.replace(" .java" , " .xml" ))
273309 val newFileLayout = File (dir, layoutName)
274310 if (newFileLayout.exists()) {
275311 flashError(R .string.msg_layout_file_exists)
276312 return
277313 }
278314
279- if (! FileIOUtils .writeFileFromString(newFileLayout, ProjectWriter .createLayout())) {
315+ if (
316+ ! FileIOUtils .writeFileFromString(
317+ newFileLayout,
318+ ProjectWriter .createLayout(),
319+ )
320+ ) {
280321 flashError(R .string.msg_layout_file_creation_failed)
281322 return
282323 }
@@ -290,7 +331,7 @@ class NewFileAction(context: Context, override val order: Int) :
290331 node,
291332 Environment .mkdirIfNotExits(file),
292333 ProjectWriter .createMenu(),
293- " .xml"
334+ " .xml" ,
294335 )
295336 }
296337
@@ -300,7 +341,7 @@ class NewFileAction(context: Context, override val order: Int) :
300341 node,
301342 Environment .mkdirIfNotExits(file),
302343 ProjectWriter .createDrawable(),
303- " .xml"
344+ " .xml" ,
304345 )
305346 }
306347
@@ -310,7 +351,7 @@ class NewFileAction(context: Context, override val order: Int) :
310351 context.getString(R .string.restype_drawable),
311352 context.getString(R .string.restype_layout),
312353 context.getString(R .string.restype_menu),
313- context.getString(R .string.restype_other)
354+ context.getString(R .string.restype_other),
314355 )
315356 val builder = DialogUtils .newMaterialDialogBuilder(context)
316357 builder.setTitle(R .string.new_xml_resource)
@@ -325,15 +366,19 @@ class NewFileAction(context: Context, override val order: Int) :
325366 builder.create().show()
326367 }
327368
328- private fun createNewEmptyFile (context : Context , node : TreeNode ? , file : File ) {
369+ private fun createNewEmptyFile (
370+ context : Context ,
371+ node : TreeNode ? ,
372+ file : File ,
373+ ) {
329374 createNewFileWithContent(context, node, file, " " )
330375 }
331376
332377 private fun createNewFileWithContent (
333378 context : Context ,
334379 node : TreeNode ? ,
335380 file : File ,
336- content : String
381+ content : String ,
337382 ) {
338383 createNewFileWithContent(context, node, file, content, null )
339384 }
@@ -345,14 +390,15 @@ class NewFileAction(context: Context, override val order: Int) :
345390 content : String ,
346391 extension : String? ,
347392 ) {
348- val binding = LayoutDialogTextInputBinding .inflate(LayoutInflater .from(context))
393+ val binding =
394+ LayoutDialogTextInputBinding .inflate(LayoutInflater .from(context))
349395 val builder = DialogUtils .newMaterialDialogBuilder(context)
350396 binding.name.editText!! .setHint(R .string.file_name)
351397 builder.setTitle(R .string.new_file)
352398 builder.setMessage(
353399 context.getString(R .string.msg_can_contain_slashes) +
354- " \n\n " +
355- context.getString(R .string.msg_newfile_dest, folder.absolutePath)
400+ " \n\n " +
401+ context.getString(R .string.msg_newfile_dest, folder.absolutePath)
356402 )
357403 builder.setView(binding.root)
358404 builder.setCancelable(false )
@@ -384,7 +430,7 @@ class NewFileAction(context: Context, override val order: Int) :
384430 node : TreeNode ? ,
385431 directory : File ,
386432 name : String ,
387- content : String
433+ content : String ,
388434 ): Boolean {
389435 if (name.length !in 1 .. 40 || name.startsWith(" /" )) {
390436 flashError(R .string.msg_invalid_name)
0 commit comments