Skip to content

Commit 14e13b0

Browse files
authored
Merge pull request #85 from zardoy/develop
2 parents 45e31b1 + a102a76 commit 14e13b0

File tree

5 files changed

+155
-9
lines changed

5 files changed

+155
-9
lines changed

README.MD

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ You can force enable this by using `Enable Strict Emmet in JSX` command.
4747

4848
#### Optional Emmet Features
4949

50-
- cleanup input & textarea suggestions
50+
- cleanup suggestions (can be enabled `jsxEmmet.modernize`)
5151
- override `.` snippet
5252

5353
Is not supported in the web for now.
@@ -157,6 +157,11 @@ Patches `toString()` insert function snippet on number types to remove tabStop.
157157

158158
Try to restore [original](https://github.com/microsoft/TypeScript/issues/49012) properties sorting in some places such as object destructure & dot property access.
159159

160+
### File Extension Suggestions
161+
162+
We extend completion list with extensions from module augmentation (e.g. `.css` files if you have `declare module '*.css'`).
163+
But for unchecked contexts list of extensions can be extended with `tsEssentialPlugins.additionalIncludeExtensions` setting.
164+
160165
### Switch Exclude Covered Cases
161166

162167
(*enabled by default*)

src/configurationType.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,19 @@ export type Configuration = {
191191
* @default true
192192
* */
193193
'removeCodeFixes.enable': boolean
194+
/**
195+
* Additional file extension to include in completions (suggestions)
196+
*
197+
* **For unchecked files only**, for checked files use module augmentation.
198+
* Example: `["css"]` or `["*"]` that will include literally every file extension
199+
* @default []
200+
*/
201+
additionalIncludeExtensions: string[]
194202
/**
195203
* @default ["fixMissingFunctionDeclaration"]
196204
* @uniqueItems true
197205
* */
198-
'removeCodeFixes.codefixes': ('fixMissingMember' | 'fixMissingProperties' | 'fixMissingAttributes' | 'fixMissingFunctionDeclaration')[]
206+
'removeCodeFixes.codefixes': FixId[]
199207
/**
200208
* Use full-blown emmet in jsx/tsx files!
201209
* Requires `jsxPseudoEmmet.enabled` to be disabled and `emmet.excludeLanguages` to have `javascriptreact` and `typescriptreact`
@@ -444,3 +452,87 @@ export type Configuration = {
444452
*/
445453
displayAdditionalInfoInCompletions: boolean
446454
}
455+
456+
// scrapped using search editor. config: caseInsesetive, context lines: 0, regex: const fix\w+ = "[^ ]+"
457+
type FixId =
458+
| 'addConvertToUnknownForNonOverlappingTypes'
459+
| 'addMissingAsync'
460+
| 'addMissingAwait'
461+
| 'addMissingConst'
462+
| 'addMissingDeclareProperty'
463+
| 'addMissingInvocationForDecorator'
464+
| 'addNameToNamelessParameter'
465+
| 'annotateWithTypeFromJSDoc'
466+
| 'fixConvertConstToLet'
467+
| 'convertFunctionToEs6Class'
468+
| 'convertLiteralTypeToMappedType'
469+
| 'convertToAsyncFunction'
470+
| 'fixConvertToMappedObjectType'
471+
| 'convertToTypeOnlyExport'
472+
| 'convertToTypeOnlyImport'
473+
| 'correctQualifiedNameToIndexedAccessType'
474+
| 'disableJsDiagnostics'
475+
| 'disableJsDiagnostics'
476+
| 'addMissingConstraint'
477+
| 'fixMissingMember'
478+
| 'fixMissingProperties'
479+
| 'fixMissingAttributes'
480+
| 'fixMissingFunctionDeclaration'
481+
| 'addMissingNewOperator'
482+
| 'fixAddModuleReferTypeMissingTypeof'
483+
| 'addVoidToPromise'
484+
| 'addVoidToPromise'
485+
| 'fixAwaitInSyncFunction'
486+
| 'fixCannotFindModule'
487+
| 'installTypesPackage'
488+
| 'fixClassDoesntImplementInheritedAbstractMember'
489+
| 'fixClassIncorrectlyImplementsInterface'
490+
| 'classSuperMustPrecedeThisAccess'
491+
| 'constructorForDerivedNeedSuperCall'
492+
| 'enableExperimentalDecorators'
493+
| 'fixEnableJsxFlag'
494+
| 'fixExpectedComma'
495+
| 'extendsInterfaceBecomesImplements'
496+
| 'forgottenThisPropertyAccess'
497+
| 'fixImplicitThis'
498+
| 'fixImportNonExportedMember'
499+
| 'fixIncorrectNamedTupleSyntax'
500+
| 'invalidImportSyntax'
501+
| 'fixInvalidJsxCharacters_expression'
502+
| 'fixInvalidJsxCharacters_htmlEntity'
503+
| 'fixJSDocTypes_plain'
504+
| 'fixJSDocTypes_nullable'
505+
| 'fixMissingCallParentheses'
506+
| 'fixNaNEquality'
507+
| 'fixNoPropertyAccessFromIndexSignature'
508+
| 'fixOverrideModifier'
509+
| 'fixAddOverrideModifier'
510+
| 'fixRemoveOverrideModifier'
511+
| 'fixPropertyAssignment'
512+
| 'fixPropertyOverrideAccessor'
513+
| 'fixReturnTypeInAsyncFunction'
514+
| 'fixSpelling'
515+
| 'strictClassInitialization'
516+
| 'addMissingPropertyDefiniteAssignmentAssertions'
517+
| 'addMissingPropertyUndefinedType'
518+
| 'addMissingPropertyInitializer'
519+
| 'fixUnreachableCode'
520+
| 'fixUnreferenceableDecoratorMetadata'
521+
| 'unusedIdentifier'
522+
| 'unusedIdentifier_prefix'
523+
| 'unusedIdentifier_delete'
524+
| 'unusedIdentifier_deleteImports'
525+
| 'unusedIdentifier_infer'
526+
| 'fixUnusedLabel'
527+
| 'inferFromUsage'
528+
| 'removeAccidentalCallParentheses'
529+
| 'removeUnnecessaryAwait'
530+
| 'requireInTs'
531+
| 'returnValueCorrect'
532+
| 'fixAddReturnStatement'
533+
| 'fixRemoveBracesFromArrowFunctionBody'
534+
| 'fixWrapTheBlockWithParen'
535+
| 'splitTypeOnlyImport'
536+
| 'useBigintLiteral'
537+
| 'useDefaultImport'
538+
| 'wrapJsxInFragment'

typescript/src/completionsAtPosition.ts

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import defaultHelpers from './completions/defaultHelpers'
2020
import objectLiteralCompletions from './completions/objectLiteralCompletions'
2121
import filterJsxElements from './completions/filterJsxComponents'
2222
import markOrRemoveGlobalCompletions from './completions/markOrRemoveGlobalLibCompletions'
23-
import { oneOf } from '@zardoy/utils'
23+
import { compact, oneOf } from '@zardoy/utils'
2424
import filterWIthIgnoreAutoImports from './completions/ignoreAutoImports'
2525
import escapeStringRegexp from 'escape-string-regexp'
2626
import addSourceDefinition from './completions/addSourceDefinition'
@@ -38,7 +38,7 @@ export const getCompletionsAtPosition = (
3838
languageService: ts.LanguageService,
3939
scriptSnapshot: ts.IScriptSnapshot,
4040
formatOptions: ts.FormatCodeSettings | undefined,
41-
additionalData: { scriptKind: ts.ScriptKind },
41+
additionalData: { scriptKind: ts.ScriptKind; compilerOptions: ts.CompilerOptions },
4242
):
4343
| {
4444
completions: ts.CompletionInfo
@@ -52,7 +52,18 @@ export const getCompletionsAtPosition = (
5252
const sourceFile = program?.getSourceFile(fileName)
5353
if (!program || !sourceFile) return
5454
if (!scriptSnapshot || isInBannedPosition(position, scriptSnapshot, sourceFile)) return
55-
let prior = languageService.getCompletionsAtPosition(fileName, position, options, formatOptions)
55+
const exactNode = findChildContainingExactPosition(sourceFile, position)
56+
const isCheckedFile =
57+
!tsFull.isSourceFileJS(sourceFile as any) || !!tsFull.isCheckJsEnabledForFile(sourceFile as any, additionalData.compilerOptions as any)
58+
const unpatch = patchBuiltinMethods(c, languageService, isCheckedFile)
59+
const getPrior = () => {
60+
try {
61+
return languageService.getCompletionsAtPosition(fileName, position, options, formatOptions)
62+
} finally {
63+
unpatch()
64+
}
65+
}
66+
let prior = getPrior()
5667
const ensurePrior = () => {
5768
if (!prior) prior = { entries: [], isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false }
5869
return true
@@ -62,7 +73,6 @@ export const getCompletionsAtPosition = (
6273
/** node that is one character behind
6374
* useful as in most cases we work with node that is behind the cursor */
6475
const leftNode = findChildContainingPosition(ts, sourceFile, position - 1)
65-
const exactNode = findChildContainingExactPosition(sourceFile, position)
6676
if (node) {
6777
// #region Fake emmet
6878
if (
@@ -327,4 +337,42 @@ const arrayMoveItemToFrom = <T>(array: T[], originalItem: ArrayPredicate<T>, ite
327337
return originalItemIndex
328338
}
329339

330-
const patchText = (input: string, start: number, end: number, newText: string) => input.slice(0, start) + newText + input.slice(end)
340+
const patchBuiltinMethods = (c: GetConfig, languageService: ts.LanguageService, isCheckedFile: boolean) => {
341+
let addFileExtensions: string[] | undefined
342+
const getAddFileExtensions = () => {
343+
const typeChecker = languageService.getProgram()!.getTypeChecker()!
344+
const ambientModules = typeChecker.getAmbientModules()
345+
/** file extensions from ambient modules declarations e.g. *.css */
346+
const fileExtensions = compact(
347+
ambientModules.map(module => {
348+
const name = module.name.slice(1, -1)
349+
if (!name.startsWith('*.') || name.includes('/')) return
350+
return name.slice(1)
351+
}),
352+
)
353+
if (!isCheckedFile) fileExtensions.push(...c('additionalIncludeExtensions').map(ext => (ext === '*' ? '' : ext)))
354+
return fileExtensions
355+
}
356+
// Its known that fuzzy completion don't work within import completions
357+
// TODO! when file name without with half-ending is typed it doesn't these completions! (seems ts bug, but probably can be fixed here)
358+
// e.g. /styles.css import './styles.c|' - no completions
359+
const oldGetSupportedExtensions = tsFull.getSupportedExtensions
360+
//@ts-expect-error monkey patch
361+
tsFull.getSupportedExtensions = (options, extraFileExtensions) => {
362+
addFileExtensions ??= getAddFileExtensions()
363+
// though I extensions could be just inlined as is
364+
return oldGetSupportedExtensions(
365+
options,
366+
extraFileExtensions?.length
367+
? extraFileExtensions
368+
: addFileExtensions.map(ext => ({
369+
extension: ext,
370+
isMixedContent: true,
371+
scriptKind: ts.ScriptKind.Deferred,
372+
})),
373+
)
374+
}
375+
return () => {
376+
tsFull.getSupportedExtensions = oldGetSupportedExtensions
377+
}
378+
}

typescript/src/decorateProxy.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ export const decorateLanguageService = (
6464
const scriptKind = languageServiceHost.getScriptKind!(fileName)
6565
// have no idea in which cases its possible, but we can't work without it
6666
if (!scriptSnapshot) return
67-
const result = getCompletionsAtPosition(fileName, position, options, c, languageService, scriptSnapshot, formatOptions, { scriptKind })
67+
const compilerOptions = languageServiceHost.getCompilationSettings()
68+
const result = getCompletionsAtPosition(fileName, position, options, c, languageService, scriptSnapshot, formatOptions, { scriptKind, compilerOptions })
6869
if (!result) return
6970
prevCompletionsMap = result.prevCompletionsMap
7071
prevCompletionsAdittionalData = result.prevCompletionsAdittionalData

typescript/test/completions.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ const getCompletionsAtPosition = (pos: number, { fileName = entrypoint, shouldHa
8484
languageService,
8585
ts.ScriptSnapshot.fromString(files[entrypoint]),
8686
undefined,
87-
{ scriptKind: ts.ScriptKind.TSX },
87+
{ scriptKind: ts.ScriptKind.TSX, compilerOptions: {} },
8888
)
8989
if (shouldHave) expect(result).not.toBeUndefined()
9090
if (!result) return

0 commit comments

Comments
 (0)