|
| 1 | +import { GetConfig } from '../types' |
| 2 | +import { dedentString, findChildContainingExactPosition, findChildContainingPositionMaxDepth } from '../utils' |
| 3 | + |
| 4 | +export const processApplicableRefactors = (refactor: ts.ApplicableRefactorInfo | undefined, c: GetConfig) => { |
| 5 | + if (!refactor) return |
| 6 | + const functionExtractors = refactor?.actions.filter(({ notApplicableReason }) => !notApplicableReason) |
| 7 | + if (functionExtractors?.length) { |
| 8 | + const kind = functionExtractors[0]!.kind! |
| 9 | + const blockScopeRefactor = functionExtractors.find(e => e.description.startsWith('Extract to inner function in')) |
| 10 | + if (blockScopeRefactor) { |
| 11 | + refactor!.actions.push({ |
| 12 | + description: 'Extract to arrow function above', |
| 13 | + kind, |
| 14 | + name: `${blockScopeRefactor.name}_local_arrow`, |
| 15 | + }) |
| 16 | + } |
| 17 | + const globalScopeRefactor = functionExtractors.find(e => e.description === 'Extract to function in global scope') |
| 18 | + if (globalScopeRefactor) { |
| 19 | + refactor!.actions.push({ |
| 20 | + description: 'Extract to arrow function in global scope above', |
| 21 | + kind, |
| 22 | + name: `${globalScopeRefactor.name}_arrow`, |
| 23 | + }) |
| 24 | + } |
| 25 | + } |
| 26 | +} |
| 27 | + |
| 28 | +export const handleFunctionRefactorEdits = ( |
| 29 | + actionName: string, |
| 30 | + |
| 31 | + languageService: ts.LanguageService, |
| 32 | + fileName: string, |
| 33 | + formatOptions: ts.FormatCodeSettings, |
| 34 | + positionOrRange: number | ts.TextRange, |
| 35 | + refactorName: string, |
| 36 | + preferences: ts.UserPreferences | undefined, |
| 37 | +): ts.RefactorEditInfo | undefined => { |
| 38 | + if (!actionName.endsWith('_arrow')) return |
| 39 | + const originalAcitonName = actionName.replace('_local_arrow', '').replace('_arrow', '') |
| 40 | + const { edits, renameLocation, renameFilename } = languageService.getEditsForRefactor( |
| 41 | + fileName, |
| 42 | + formatOptions, |
| 43 | + positionOrRange, |
| 44 | + refactorName, |
| 45 | + originalAcitonName, |
| 46 | + preferences, |
| 47 | + )! |
| 48 | + // has random number of edits because imports can be added |
| 49 | + const { textChanges } = edits[0]! |
| 50 | + const functionChange = textChanges.at(-1)! |
| 51 | + functionChange.newText = functionChange.newText |
| 52 | + .replace(/function /, 'const ') |
| 53 | + .replace('(', ' = (') |
| 54 | + .replace(/\{\n/, '=> {\n') |
| 55 | + |
| 56 | + const isLocal = actionName.endsWith('_local_arrow') |
| 57 | + // to think: maybe reuse ts getNodeToInsertPropertyBefore instead? |
| 58 | + const constantEdits = isLocal |
| 59 | + ? languageService.getEditsForRefactor(fileName, formatOptions, positionOrRange, refactorName, 'constant_scope_0', preferences)!.edits |
| 60 | + : undefined |
| 61 | + // local scope |
| 62 | + if (constantEdits) { |
| 63 | + const constantAdd = constantEdits[0]!.textChanges[0]! |
| 64 | + functionChange.span.start = constantAdd.span.start |
| 65 | + const indent = constantAdd.newText.match(/^\s*/)![0] |
| 66 | + // fix indent |
| 67 | + functionChange.newText = dedentString(functionChange.newText, indent, true) + '\n' |
| 68 | + } |
| 69 | + |
| 70 | + // global scope |
| 71 | + if (!isLocal) { |
| 72 | + const lastNode = findChildContainingPositionMaxDepth( |
| 73 | + languageService.getProgram()!.getSourceFile(fileName)!, |
| 74 | + typeof positionOrRange === 'object' ? positionOrRange.pos : positionOrRange, |
| 75 | + 2, |
| 76 | + ) |
| 77 | + if (lastNode) { |
| 78 | + const pos = lastNode.pos + (lastNode.getFullText().match(/^\s+/)?.[0]?.length ?? 1) - 1 |
| 79 | + functionChange.span.start = pos |
| 80 | + } |
| 81 | + } |
| 82 | + return { |
| 83 | + edits: [{ fileName, textChanges }], |
| 84 | + // TODO since ts making edit after current location, it doesn't expect renameLocation to be changed (lets fix it) |
| 85 | + // renameLocation: renameLocation! + functionChange.newText.length + 1, |
| 86 | + renameLocation: functionChange.span.start + functionChange.newText.indexOf('const ') + 'const '.length, |
| 87 | + renameFilename, |
| 88 | + } |
| 89 | +} |
0 commit comments