Skip to content

Commit

Permalink
Merge pull request #85 from zardoy/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
zardoy authored Jan 24, 2023
2 parents 45e31b1 + a102a76 commit 14e13b0
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 9 deletions.
7 changes: 6 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ You can force enable this by using `Enable Strict Emmet in JSX` command.

#### Optional Emmet Features

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

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

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

### File Extension Suggestions

We extend completion list with extensions from module augmentation (e.g. `.css` files if you have `declare module '*.css'`).
But for unchecked contexts list of extensions can be extended with `tsEssentialPlugins.additionalIncludeExtensions` setting.

### Switch Exclude Covered Cases

(*enabled by default*)
Expand Down
94 changes: 93 additions & 1 deletion src/configurationType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,19 @@ export type Configuration = {
* @default true
* */
'removeCodeFixes.enable': boolean
/**
* Additional file extension to include in completions (suggestions)
*
* **For unchecked files only**, for checked files use module augmentation.
* Example: `["css"]` or `["*"]` that will include literally every file extension
* @default []
*/
additionalIncludeExtensions: string[]
/**
* @default ["fixMissingFunctionDeclaration"]
* @uniqueItems true
* */
'removeCodeFixes.codefixes': ('fixMissingMember' | 'fixMissingProperties' | 'fixMissingAttributes' | 'fixMissingFunctionDeclaration')[]
'removeCodeFixes.codefixes': FixId[]
/**
* Use full-blown emmet in jsx/tsx files!
* Requires `jsxPseudoEmmet.enabled` to be disabled and `emmet.excludeLanguages` to have `javascriptreact` and `typescriptreact`
Expand Down Expand Up @@ -444,3 +452,87 @@ export type Configuration = {
*/
displayAdditionalInfoInCompletions: boolean
}

// scrapped using search editor. config: caseInsesetive, context lines: 0, regex: const fix\w+ = "[^ ]+"
type FixId =
| 'addConvertToUnknownForNonOverlappingTypes'
| 'addMissingAsync'
| 'addMissingAwait'
| 'addMissingConst'
| 'addMissingDeclareProperty'
| 'addMissingInvocationForDecorator'
| 'addNameToNamelessParameter'
| 'annotateWithTypeFromJSDoc'
| 'fixConvertConstToLet'
| 'convertFunctionToEs6Class'
| 'convertLiteralTypeToMappedType'
| 'convertToAsyncFunction'
| 'fixConvertToMappedObjectType'
| 'convertToTypeOnlyExport'
| 'convertToTypeOnlyImport'
| 'correctQualifiedNameToIndexedAccessType'
| 'disableJsDiagnostics'
| 'disableJsDiagnostics'
| 'addMissingConstraint'
| 'fixMissingMember'
| 'fixMissingProperties'
| 'fixMissingAttributes'
| 'fixMissingFunctionDeclaration'
| 'addMissingNewOperator'
| 'fixAddModuleReferTypeMissingTypeof'
| 'addVoidToPromise'
| 'addVoidToPromise'
| 'fixAwaitInSyncFunction'
| 'fixCannotFindModule'
| 'installTypesPackage'
| 'fixClassDoesntImplementInheritedAbstractMember'
| 'fixClassIncorrectlyImplementsInterface'
| 'classSuperMustPrecedeThisAccess'
| 'constructorForDerivedNeedSuperCall'
| 'enableExperimentalDecorators'
| 'fixEnableJsxFlag'
| 'fixExpectedComma'
| 'extendsInterfaceBecomesImplements'
| 'forgottenThisPropertyAccess'
| 'fixImplicitThis'
| 'fixImportNonExportedMember'
| 'fixIncorrectNamedTupleSyntax'
| 'invalidImportSyntax'
| 'fixInvalidJsxCharacters_expression'
| 'fixInvalidJsxCharacters_htmlEntity'
| 'fixJSDocTypes_plain'
| 'fixJSDocTypes_nullable'
| 'fixMissingCallParentheses'
| 'fixNaNEquality'
| 'fixNoPropertyAccessFromIndexSignature'
| 'fixOverrideModifier'
| 'fixAddOverrideModifier'
| 'fixRemoveOverrideModifier'
| 'fixPropertyAssignment'
| 'fixPropertyOverrideAccessor'
| 'fixReturnTypeInAsyncFunction'
| 'fixSpelling'
| 'strictClassInitialization'
| 'addMissingPropertyDefiniteAssignmentAssertions'
| 'addMissingPropertyUndefinedType'
| 'addMissingPropertyInitializer'
| 'fixUnreachableCode'
| 'fixUnreferenceableDecoratorMetadata'
| 'unusedIdentifier'
| 'unusedIdentifier_prefix'
| 'unusedIdentifier_delete'
| 'unusedIdentifier_deleteImports'
| 'unusedIdentifier_infer'
| 'fixUnusedLabel'
| 'inferFromUsage'
| 'removeAccidentalCallParentheses'
| 'removeUnnecessaryAwait'
| 'requireInTs'
| 'returnValueCorrect'
| 'fixAddReturnStatement'
| 'fixRemoveBracesFromArrowFunctionBody'
| 'fixWrapTheBlockWithParen'
| 'splitTypeOnlyImport'
| 'useBigintLiteral'
| 'useDefaultImport'
| 'wrapJsxInFragment'
58 changes: 53 additions & 5 deletions typescript/src/completionsAtPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import defaultHelpers from './completions/defaultHelpers'
import objectLiteralCompletions from './completions/objectLiteralCompletions'
import filterJsxElements from './completions/filterJsxComponents'
import markOrRemoveGlobalCompletions from './completions/markOrRemoveGlobalLibCompletions'
import { oneOf } from '@zardoy/utils'
import { compact, oneOf } from '@zardoy/utils'
import filterWIthIgnoreAutoImports from './completions/ignoreAutoImports'
import escapeStringRegexp from 'escape-string-regexp'
import addSourceDefinition from './completions/addSourceDefinition'
Expand All @@ -38,7 +38,7 @@ export const getCompletionsAtPosition = (
languageService: ts.LanguageService,
scriptSnapshot: ts.IScriptSnapshot,
formatOptions: ts.FormatCodeSettings | undefined,
additionalData: { scriptKind: ts.ScriptKind },
additionalData: { scriptKind: ts.ScriptKind; compilerOptions: ts.CompilerOptions },
):
| {
completions: ts.CompletionInfo
Expand All @@ -52,7 +52,18 @@ export const getCompletionsAtPosition = (
const sourceFile = program?.getSourceFile(fileName)
if (!program || !sourceFile) return
if (!scriptSnapshot || isInBannedPosition(position, scriptSnapshot, sourceFile)) return
let prior = languageService.getCompletionsAtPosition(fileName, position, options, formatOptions)
const exactNode = findChildContainingExactPosition(sourceFile, position)
const isCheckedFile =
!tsFull.isSourceFileJS(sourceFile as any) || !!tsFull.isCheckJsEnabledForFile(sourceFile as any, additionalData.compilerOptions as any)
const unpatch = patchBuiltinMethods(c, languageService, isCheckedFile)
const getPrior = () => {
try {
return languageService.getCompletionsAtPosition(fileName, position, options, formatOptions)
} finally {
unpatch()
}
}
let prior = getPrior()
const ensurePrior = () => {
if (!prior) prior = { entries: [], isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false }
return true
Expand All @@ -62,7 +73,6 @@ export const getCompletionsAtPosition = (
/** node that is one character behind
* useful as in most cases we work with node that is behind the cursor */
const leftNode = findChildContainingPosition(ts, sourceFile, position - 1)
const exactNode = findChildContainingExactPosition(sourceFile, position)
if (node) {
// #region Fake emmet
if (
Expand Down Expand Up @@ -327,4 +337,42 @@ const arrayMoveItemToFrom = <T>(array: T[], originalItem: ArrayPredicate<T>, ite
return originalItemIndex
}

const patchText = (input: string, start: number, end: number, newText: string) => input.slice(0, start) + newText + input.slice(end)
const patchBuiltinMethods = (c: GetConfig, languageService: ts.LanguageService, isCheckedFile: boolean) => {
let addFileExtensions: string[] | undefined
const getAddFileExtensions = () => {
const typeChecker = languageService.getProgram()!.getTypeChecker()!
const ambientModules = typeChecker.getAmbientModules()
/** file extensions from ambient modules declarations e.g. *.css */
const fileExtensions = compact(
ambientModules.map(module => {
const name = module.name.slice(1, -1)
if (!name.startsWith('*.') || name.includes('/')) return
return name.slice(1)
}),
)
if (!isCheckedFile) fileExtensions.push(...c('additionalIncludeExtensions').map(ext => (ext === '*' ? '' : ext)))
return fileExtensions
}
// Its known that fuzzy completion don't work within import completions
// TODO! when file name without with half-ending is typed it doesn't these completions! (seems ts bug, but probably can be fixed here)
// e.g. /styles.css import './styles.c|' - no completions
const oldGetSupportedExtensions = tsFull.getSupportedExtensions
//@ts-expect-error monkey patch
tsFull.getSupportedExtensions = (options, extraFileExtensions) => {
addFileExtensions ??= getAddFileExtensions()
// though I extensions could be just inlined as is
return oldGetSupportedExtensions(
options,
extraFileExtensions?.length
? extraFileExtensions
: addFileExtensions.map(ext => ({
extension: ext,
isMixedContent: true,
scriptKind: ts.ScriptKind.Deferred,
})),
)
}
return () => {
tsFull.getSupportedExtensions = oldGetSupportedExtensions
}
}
3 changes: 2 additions & 1 deletion typescript/src/decorateProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export const decorateLanguageService = (
const scriptKind = languageServiceHost.getScriptKind!(fileName)
// have no idea in which cases its possible, but we can't work without it
if (!scriptSnapshot) return
const result = getCompletionsAtPosition(fileName, position, options, c, languageService, scriptSnapshot, formatOptions, { scriptKind })
const compilerOptions = languageServiceHost.getCompilationSettings()
const result = getCompletionsAtPosition(fileName, position, options, c, languageService, scriptSnapshot, formatOptions, { scriptKind, compilerOptions })
if (!result) return
prevCompletionsMap = result.prevCompletionsMap
prevCompletionsAdittionalData = result.prevCompletionsAdittionalData
Expand Down
2 changes: 1 addition & 1 deletion typescript/test/completions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const getCompletionsAtPosition = (pos: number, { fileName = entrypoint, shouldHa
languageService,
ts.ScriptSnapshot.fromString(files[entrypoint]),
undefined,
{ scriptKind: ts.ScriptKind.TSX },
{ scriptKind: ts.ScriptKind.TSX, compilerOptions: {} },
)
if (shouldHave) expect(result).not.toBeUndefined()
if (!result) return
Expand Down

0 comments on commit 14e13b0

Please sign in to comment.