diff --git a/annotation-processors/.gitignore b/annotation-processors/.gitignore
new file mode 100644
index 000000000..b63da4551
--- /dev/null
+++ b/annotation-processors/.gitignore
@@ -0,0 +1,42 @@
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/annotation-processors/build.gradle.kts b/annotation-processors/build.gradle.kts
new file mode 100644
index 000000000..8f0b8ac51
--- /dev/null
+++ b/annotation-processors/build.gradle.kts
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+plugins {
+  kotlin("jvm")
+}
+
+group = "com.intellij"
+version = "SNAPSHOT"
+
+repositories {
+  mavenCentral()
+}
+
+dependencies {
+  implementation("com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.8")
+  implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.15.0")
+}
\ No newline at end of file
diff --git a/annotation-processors/src/main/kotlin/com/intellij/vim/FileWriter.kt b/annotation-processors/src/main/kotlin/com/intellij/vim/FileWriter.kt
new file mode 100644
index 000000000..550a1f9ff
--- /dev/null
+++ b/annotation-processors/src/main/kotlin/com/intellij/vim/FileWriter.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.intellij.vim
+
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import java.io.File
+
+class FileWriter {
+  fun generateResourceFile(fileName: String, content: String, environment: SymbolProcessorEnvironment) {
+    val resourcesDir = environment.options["resourcesDir"]
+    val file = File("$resourcesDir/$fileName")
+    file.writeText(content)
+  }
+}
\ No newline at end of file
diff --git a/annotation-processors/src/main/kotlin/com/intellij/vim/annotations/VimscriptFunction.kt b/annotation-processors/src/main/kotlin/com/intellij/vim/annotations/VimscriptFunction.kt
new file mode 100644
index 000000000..43045e530
--- /dev/null
+++ b/annotation-processors/src/main/kotlin/com/intellij/vim/annotations/VimscriptFunction.kt
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.intellij.vim.annotations
+
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.SOURCE)
+annotation class VimscriptFunction(val name: String)
\ No newline at end of file
diff --git a/annotation-processors/src/main/kotlin/com/intellij/vim/model/LazyInstance.kt b/annotation-processors/src/main/kotlin/com/intellij/vim/model/LazyInstance.kt
new file mode 100644
index 000000000..d900a88c3
--- /dev/null
+++ b/annotation-processors/src/main/kotlin/com/intellij/vim/model/LazyInstance.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.intellij.vim.model
+
+import java.lang.invoke.MethodHandles
+import java.lang.invoke.MethodType
+
+open class LazyInstance<T>(private val className: String, private val classLoader: ClassLoader) {
+  val instance: T by lazy {
+    val aClass = classLoader.loadClass(className)
+    val lookup = MethodHandles.privateLookupIn(aClass, MethodHandles.lookup())
+    val instance = lookup.findConstructor(aClass, MethodType.methodType(Void.TYPE)).invoke()
+    @Suppress("UNCHECKED_CAST")
+    instance as T
+  }
+}
\ No newline at end of file
diff --git a/annotation-processors/src/main/kotlin/com/intellij/vim/processors/VimscriptFunctionProcessor.kt b/annotation-processors/src/main/kotlin/com/intellij/vim/processors/VimscriptFunctionProcessor.kt
new file mode 100644
index 000000000..f577c65e2
--- /dev/null
+++ b/annotation-processors/src/main/kotlin/com/intellij/vim/processors/VimscriptFunctionProcessor.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.intellij.vim.processors
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.getAnnotationsByType
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFile
+import com.google.devtools.ksp.symbol.KSVisitorVoid
+import com.intellij.vim.FileWriter
+import com.intellij.vim.annotations.VimscriptFunction
+
+class VimscriptFunctionProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor {
+  private val visitor = VimscriptFunctionVisitor()
+  private val writer = FileWriter()
+  private val nameToFunction = mutableMapOf<String, KSClassDeclaration>()
+
+
+  override fun process(resolver: Resolver): List<KSAnnotated> {
+    resolver.getAllFiles().forEach { it.accept(visitor, Unit) }
+    writer.generateResourceFile("VimscriptFunctions.yaml", generateFunctionDict(), environment)
+    return emptyList()
+  }
+
+  private fun generateFunctionDict(): String {
+    val mapper = YAMLMapper()
+    val dictToWrite: Map<String, String> = nameToFunction
+      .map { it.key to it.value.qualifiedName!!.asString() }
+      .toMap()
+    return mapper.writeValueAsString(dictToWrite)
+  }
+
+  // todo inspection that annotation is properly used on proper classes
+  private inner class VimscriptFunctionVisitor : KSVisitorVoid() {
+    @OptIn(KspExperimental::class)
+    override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
+      val vimscriptFunctionAnnotation = classDeclaration.getAnnotationsByType(VimscriptFunction::class).firstOrNull() ?: return
+      val functionName = vimscriptFunctionAnnotation.name
+      nameToFunction[functionName] = classDeclaration
+    }
+
+    override fun visitFile(file: KSFile, data: Unit) {
+      file.declarations.forEach { it.accept(this, Unit) }
+    }
+  }
+}
+
diff --git a/annotation-processors/src/main/kotlin/com/intellij/vim/providers/VimscriptFunctionProcessorProvider.kt b/annotation-processors/src/main/kotlin/com/intellij/vim/providers/VimscriptFunctionProcessorProvider.kt
new file mode 100644
index 000000000..81b19390e
--- /dev/null
+++ b/annotation-processors/src/main/kotlin/com/intellij/vim/providers/VimscriptFunctionProcessorProvider.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.intellij.vim.providers
+
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.processing.SymbolProcessorProvider
+import com.intellij.vim.processors.VimscriptFunctionProcessor
+
+public class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
+  override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
+    return VimscriptFunctionProcessor(environment)
+  }
+}
\ No newline at end of file
diff --git a/annotation-processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/annotation-processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 000000000..aa17eb85c
--- /dev/null
+++ b/annotation-processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+com.intellij.vim.providers.VimscriptFunctionProcessorProvider
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 41858e268..ed2891bd8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -11,4 +11,5 @@ pluginManagement {
 rootProject.name = 'IdeaVIM'
 include 'vim-engine'
 include 'scripts'
+include 'annotation-processors'
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimscriptFunctionService.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimscriptFunctionService.kt
index 3938eb40a..218dd8882 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimscriptFunctionService.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimscriptFunctionService.kt
@@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.api
 import com.maddyhome.idea.vim.vimscript.model.VimLContext
 import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
 import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler
+import com.maddyhome.idea.vim.vimscript.model.functions.LazyVimscriptFunction
 import com.maddyhome.idea.vim.vimscript.model.statements.FunctionDeclaration
 
 public interface VimscriptFunctionService {
@@ -22,5 +23,5 @@ public interface VimscriptFunctionService {
   public fun getUserDefinedFunction(scope: Scope?, name: String, vimContext: VimLContext): FunctionDeclaration?
   public fun getBuiltInFunction(name: String): FunctionHandler?
   public fun registerHandlers()
-  public fun addHandler(handlerHolder: Any)
+  public fun addHandler(handler: LazyVimscriptFunction)
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/LazyVimscriptFunction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/LazyVimscriptFunction.kt
new file mode 100644
index 000000000..7189f9ed3
--- /dev/null
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/LazyVimscriptFunction.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.maddyhome.idea.vim.vimscript.model.functions
+
+import com.intellij.vim.model.LazyInstance
+
+public class LazyVimscriptFunction(public val name: String, className: String, classLoader: ClassLoader):
+  LazyInstance<FunctionHandler>(className, classLoader)
\ No newline at end of file
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/VimscriptFunctionProvider.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/VimscriptFunctionProvider.kt
new file mode 100644
index 000000000..985cfc10a
--- /dev/null
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/VimscriptFunctionProvider.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.maddyhome.idea.vim.vimscript.model.functions
+
+import com.fasterxml.jackson.core.type.TypeReference
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper
+import java.io.InputStream
+import javax.swing.InputMap
+
+public interface VimscriptFunctionProvider {
+  public val functionListFile: InputStream
+
+  public fun getFunctions(): Collection<LazyVimscriptFunction> {
+    val mapper = YAMLMapper()
+    val classLoader = this.javaClass.classLoader
+    val typeReference = object : TypeReference<HashMap<String, String>>() {}
+    val functionDict = mapper.readValue(functionListFile, typeReference)
+    return functionDict.map { LazyVimscriptFunction(it.key, it.value, classLoader) }
+  }
+}
\ No newline at end of file