From 8f3280ffcbd956eb7f7fc03fcb525fbd019e6a6f Mon Sep 17 00:00:00 2001
From: Vitaly Turovsky <vital2580@icloud.com>
Date: Sun, 11 Sep 2022 12:02:52 +0300
Subject: [PATCH] feat: big feature! Args snippets for simple arrow callbacks
 (enabled by default). Feature is experimental and doesn't support aliases for
 now

---
 src/configurationType.ts |  5 +++++
 src/extension.ts         | 32 ++++++++++++++++++++++++++++++++
 typescript/src/index.ts  | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)

diff --git a/src/configurationType.ts b/src/configurationType.ts
index d5bdf73d..45f24a41 100644
--- a/src/configurationType.ts
+++ b/src/configurationType.ts
@@ -153,4 +153,9 @@ export type Configuration = {
      * @default false
      */
     changeDtsFileDefinitionToJs: boolean
+    /**
+     * Experimental. Also includes optional args
+     * @default true
+     */
+    enableMethodSnippets: boolean
 }
diff --git a/src/extension.ts b/src/extension.ts
index 30e31a66..f876f9e5 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,4 +1,6 @@
 import * as vscode from 'vscode'
+import { getActiveRegularEditor } from '@zardoy/vscode-utils'
+import {} from 'vscode-framework'
 
 export const activate = async () => {
     const tsExtension = vscode.extensions.getExtension('vscode.typescript-language-features')
@@ -21,4 +23,34 @@ export const activate = async () => {
         if (affectsConfiguration(process.env.IDS_PREFIX!)) syncConfig()
     })
     syncConfig()
+
+    api.onCompletionAccepted((item: vscode.CompletionItem & { document: vscode.TextDocument }) => {
+        const enableMethodSnippets = vscode.workspace.getConfiguration(process.env.IDS_PREFIX, item.document).get('enableMethodSnippets')
+        const { documentation = '' } = item
+        const documentationString = documentation instanceof vscode.MarkdownString ? documentation.value : documentation
+        const insertFuncArgs = /<!-- insert-func: (.*)-->/.exec(documentationString)?.[1]
+        if (enableMethodSnippets && insertFuncArgs !== undefined) {
+            const editor = getActiveRegularEditor()!
+            const startPos = editor.selection.start
+            const nextSymbol = editor.document.getText(new vscode.Range(startPos, startPos.translate(0, 1)))
+            if (nextSymbol !== '(') {
+                const snippet = new vscode.SnippetString('')
+                snippet.appendText('(')
+                const args = insertFuncArgs.split(',')
+                for (const [i, arg] of args.entries()) {
+                    if (!arg) continue
+                    snippet.appendPlaceholder(arg)
+                    if (i !== args.length - 1) snippet.appendText(', ')
+                }
+
+                snippet.appendText(')')
+                void editor.insertSnippet(snippet, undefined, {
+                    undoStopAfter: false,
+                    undoStopBefore: false,
+                })
+                if (vscode.workspace.getConfiguration('editor.parameterHints').get('enabled'))
+                    void vscode.commands.executeCommand('editor.action.triggerParameterHints')
+            }
+        }
+    })
 }
diff --git a/typescript/src/index.ts b/typescript/src/index.ts
index 0cbf1368..4f849629 100644
--- a/typescript/src/index.ts
+++ b/typescript/src/index.ts
@@ -6,6 +6,9 @@ import type { Configuration } from '../../src/configurationType'
 import _ from 'lodash'
 import { GetConfig } from './types'
 import { getCompletionsAtPosition, PrevCompletionMap } from './completionsAtPosition'
+import { CompletionEntry } from 'typescript/lib/tsserverlibrary'
+import { getParameterListParts } from './completionGetMethodParameters'
+import { oneOf } from '@zardoy/utils'
 
 const thisPluginMarker = Symbol('__essentialPluginsMarker__')
 
@@ -83,6 +86,35 @@ export = function ({ typescript }: { typescript: typeof import('typescript/lib/t
                     data,
                 )
                 if (!prior) return
+                if (c('enableMethodSnippets') && oneOf(prior.kind as string, ts.ScriptElementKind.constElement, 'property')) {
+                    const punctuationIndex = prior.displayParts.findIndex(({ kind }) => kind === 'punctuation')
+                    if (punctuationIndex !== 1) {
+                        const isParsableMethod = prior.displayParts
+                            // next is space
+                            .slice(punctuationIndex + 2)
+                            .map(({ text }) => text)
+                            .join('')
+                            .match(/\((.*)\) => /)
+                        if (isParsableMethod) {
+                            let firstArgMeet = false
+                            const args = prior.displayParts
+                                .filter(({ kind }, index, array) => {
+                                    if (kind !== 'parameterName') return false
+                                    if (array[index - 1]!.text === '(') {
+                                        if (!firstArgMeet) {
+                                            // bad parsing, as doesn't take second and more args
+                                            firstArgMeet = true
+                                            return true
+                                        }
+                                        return false
+                                    }
+                                    return true
+                                })
+                                .map(({ text }) => text)
+                            prior.documentation = [...(prior.documentation ?? []), { kind: 'text', text: `<!-- insert-func: ${args.join(',')}-->` }]
+                        }
+                    }
+                }
                 // if (prior.kind === typescript.ScriptElementKind.constElement && prior.displayParts.map(item => item.text).join('').match(/: \(.+\) => .+/)) prior.codeActions?.push({
                 //     description: '',
                 //     changes: []