Skip to content

Commit

Permalink
Merge pull request #137 from zardoy/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
zardoy authored Jun 21, 2023
2 parents 9941886 + f76e81b commit d0b9de8
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 42 deletions.
4 changes: 3 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ TOC:
- [Contributed Code Actions](#contributed-code-actions)
- [Even Even More](#even-even-more)

> *Note* Visit website for list of recommended settings: <https://ts-plugin.zardoy.com/>
> *Note*: You can disable all optional features with `> Disable All Optional Features` setting right after install.
>
> *Note*: Visit website for list of recommended settings: <https://ts-plugin.zardoy.com/>
## Top Features

Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
{
"command": "pasteCodeWithImports",
"title": "Paste Code with Imports"
},
{
"command": "disableAllOptionalFeatures",
"title": "Disable All Optional Features"
}
],
"keybindings": [
Expand Down Expand Up @@ -90,7 +94,7 @@
"keywords": [
"ts",
"javascript",
"plugin",
"pro",
"webstorm",
"typescript hero"
],
Expand Down
31 changes: 17 additions & 14 deletions src/configurationType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export type Configuration = {
/**
* Will be `format-short` by default in future as super useful!
* Requires TypeScript 5.0+
* @recommended
* @default disable
*/
'suggestions.displayImportedInfo': 'disable' | 'short-format' | 'long-format'
Expand Down Expand Up @@ -160,14 +161,15 @@ export type Configuration = {
* */
'correctSorting.enable': boolean
/**
* Try to restore suggestion sorting after `.`
* Try to restore properties (not variables!) sorting as in source
* Experimental and most probably will be changed in future
* @recommended
* @default false
*/
fixSuggestionsSorting: boolean
// TODO
// TODO-low
/**
* Mark QuickFixes & refactorings with 🔵
* Mark refactorings with 🔵
* @default true
*/
'markTsCodeActions.enable': boolean
Expand Down Expand Up @@ -205,6 +207,11 @@ export type Configuration = {
* @default true
* */
'removeCodeFixes.enable': boolean
/**
* @default ["fixMissingFunctionDeclaration"]
* @uniqueItems true
* */
'removeCodeFixes.codefixes': FixId[]
/**
* Also rename name of default or namespace import on refactor caused by file move / rename
* Probably will be enabled by default in future
Expand All @@ -228,13 +235,8 @@ export type Configuration = {
*/
workspaceSymbolSearchExcludePatterns: string[]
/**
* @default ["fixMissingFunctionDeclaration"]
* @uniqueItems true
* */
'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`
* Use strict & precise emmet in jsx/tsx files! Doesn't annoy you everywhere!
* Requires `jsxPseudoEmmet.enabled` to be disabled and `emmet.excludeLanguages` to have `javascriptreact` / `typescriptreact`
* @default true
* */
'jsxEmmet.enable': boolean
Expand Down Expand Up @@ -443,11 +445,11 @@ export type Configuration = {
*/
// completionHelpers: boolean
/**
* Extend TypeScript outline!
* Extend outline with:
* Extend TypeScript outline with:
* - JSX Elements
* - Type Alias Declarations
* Should be stable!
* @recommended
* @default false
*/
patchOutline: boolean
Expand All @@ -458,6 +460,7 @@ export type Configuration = {
'outline.arraysTuplesNumberedItems': boolean
/**
* Exclude covered strings/enum cases in switch in completions
* @deprecated Will be removed in next release
* @default true
*/
switchExcludeCoveredCases: boolean
Expand Down Expand Up @@ -520,12 +523,12 @@ export type Configuration = {
*/
'figIntegration.enableWhenStartsWith': string[]
/**
* Propose additional completions in object. Just like `typescript.suggest.objectLiteralMethodSnippets.enabled`, but also for string, arrays and objects
* Propose additional completions in object. Just like `typescript.suggest.objectLiteralMethodSnippets.enabled`, but also for strings, arrays and objects
* @default true
*/
'objectLiteralCompletions.moreVariants': boolean
/**
* When `moreVariants` enabled, always add as fallback variant if other variant can't be derived
* When `moreVariants` is enabled, always add fallback variant (`: `) if other variant can't be derived
* @default false
*/
'objectLiteralCompletions.fallbackVariant': boolean
Expand Down
56 changes: 55 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable @typescript-eslint/no-require-imports */
import * as vscode from 'vscode'
import { defaultJsSupersetLangs } from '@zardoy/vscode-utils/build/langs'
import { extensionCtx, getExtensionSetting, getExtensionSettingId } from 'vscode-framework'
import { Settings, extensionCtx, getExtensionSetting, getExtensionSettingId, registerExtensionCommand } from 'vscode-framework'
import { pickObj } from '@zardoy/utils'
import { watchExtensionSettings } from '@zardoy/vscode-utils/build/settings'
import { ConditionalPick } from 'type-fest'
import webImports from './webImports'
import { sendCommand } from './sendCommand'
import { registerEmmet } from './emmet'
Expand Down Expand Up @@ -90,6 +91,7 @@ export const activateTsPlugin = (tsApi: { configurePlugin; onCompletionAccepted
}

export const activate = async () => {
registerDisableOptionalFeaturesCommand()
migrateSettings()

const possiblyActivateTsPlugin = async () => {
Expand Down Expand Up @@ -138,3 +140,55 @@ export const activate = async () => {
})
}
}

const registerDisableOptionalFeaturesCommand = () => {
registerExtensionCommand('disableAllOptionalFeatures', async () => {
const config = vscode.workspace.getConfiguration(process.env.IDS_PREFIX, null)
const toDisable: Array<[keyof Settings, any]> = []
for (const optionalExperience of optionalExperiences) {
const desiredKey = Array.isArray(optionalExperience) ? optionalExperience[0] : optionalExperience
const desiredValue = Array.isArray(optionalExperience) ? optionalExperience[1] : false
if (config.get(desiredKey) !== desiredValue) toDisable.push([desiredKey, desiredValue])
}

const action = await vscode.window.showInformationMessage(
`${toDisable.length} features are going to be disabled`,
{ detail: '', modal: true },
'Write to settings NOW',
'Copy settings',
)
if (!action) return
switch (action) {
case 'Write to settings NOW': {
for (const [key, value] of toDisable) {
void config.update(key, value, vscode.ConfigurationTarget.Global)
}

break
}

case 'Copy settings': {
await vscode.env.clipboard.writeText(JSON.stringify(Object.fromEntries(toDisable), undefined, 4))
break
}
}
})
}

/** Experiences that are enabled out of the box */
const optionalExperiences: Array<keyof ConditionalPick<Settings, boolean> | [keyof Settings, any]> = [
'enableMethodSnippets',
'removeUselessFunctionProps.enable',
'patchToString.enable',
['suggestions.keywordsInsertText', 'none'],
'highlightNonFunctionMethods.enable',
'markTsCodeActions.enable',
['markTsCodeFixes.character', ''],
'removeCodeFixes.enable',
'removeDefinitionFromReferences',
'removeImportsFromReferences',
'miscDefinitionImprovement',
'improveJsxCompletions',
'objectLiteralCompletions.moreVariants',
'codeActions.extractTypeInferName',
]
2 changes: 1 addition & 1 deletion typescript/src/codeActions/decorateProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default (proxy: ts.LanguageService, languageService: ts.LanguageService,
const program = languageService.getProgram()!
const sourceFile = program.getSourceFile(fileName)!
processApplicableRefactors(
prior.find(r => r.description === 'Extract function'),
prior.find(r => r.name === 'Extract Symbol' && r.actions.some(action => action.kind?.startsWith('refactor.extract.function')))?.actions,
c,
positionOrRange,
sourceFile,
Expand Down
23 changes: 12 additions & 11 deletions typescript/src/codeActions/functionExtractors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { equals } from 'rambda'
import { GetConfig } from '../types'
import {
createDummySourceFile,
Expand All @@ -9,16 +8,16 @@ import {
} from '../utils'

export const processApplicableRefactors = (
refactor: ts.ApplicableRefactorInfo | undefined,
refactorActions: ts.RefactorActionInfo[] | undefined,
c: GetConfig,
posOrRange: number | ts.TextRange,
sourceFile: ts.SourceFile,
) => {
if (!refactor) return
const functionExtractors = refactor?.actions.filter(({ notApplicableReason }) => !notApplicableReason)
if (!refactorActions) return
const functionExtractors = refactorActions.filter(({ notApplicableReason }) => !notApplicableReason)
if (functionExtractors?.length) {
const kind = functionExtractors[0]!.kind!
const blockScopeRefactor = functionExtractors.find(e => e.description.startsWith('Extract to inner function in'))
const blockScopeRefactor = functionExtractors.find(e => e.description.includes('inner function'))
const addArrowCodeActions: ts.RefactorActionInfo[] = []
if (blockScopeRefactor) {
addArrowCodeActions.push({
Expand All @@ -28,9 +27,7 @@ export const processApplicableRefactors = (
})
}
let addExtractToJsxRefactor = false
const globalScopeRefactor = functionExtractors.find(e =>
['Extract to function in global scope', 'Extract to function in module scope'].includes(e.description),
)
const globalScopeRefactor = functionExtractors.at(-1)
if (globalScopeRefactor) {
addArrowCodeActions.push({
description: 'Extract to arrow function in global scope above',
Expand All @@ -42,16 +39,20 @@ export const processApplicableRefactors = (
}

if (addExtractToJsxRefactor) {
refactor.actions = refactor.actions.filter(action => !action.name.startsWith('function_scope'))
refactor.actions.push({
for (const refactorAction of refactorActions) {
if (refactorAction.name.startsWith('function_scope')) {
refactorAction.notApplicableReason = 'JSX Element Selected. Use Extract to JSX component'
}
}
refactorActions.push({
description: 'Extract to JSX component',
kind: 'refactor.extract.jsx',
name: `${globalScopeRefactor!.name}_jsx`,
})
return
}

refactor.actions.push(...addArrowCodeActions)
refactorActions.push(...addArrowCodeActions)
}
}

Expand Down
29 changes: 27 additions & 2 deletions typescript/src/completions/boostNameSuggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,37 @@ export default (
const fileText = sourceFile.getFullText()
const fileTextBeforePos = fileText.slice(0, position)
const beforeConstNodeOffset = fileTextBeforePos.match(/(?:const|let) ([\w\d]*)$/i)?.[1]
const nodeWithStatements = node => {
return node && 'statements' in node && Array.isArray(node.statements) ? node : undefined
}
const statementsNode = nodeWithStatements(node) || nodeWithStatements(node.parent)
// Workaround for current locality bonus & TS 5.1
if (statementsNode) {
const statements = statementsNode.statements as any[]
const prevNode =
statementsNode === node
? [...statements].reverse().find((statement: ts.Node) => statement.pos + statement.getLeadingTriviaWidth() < position)
: statements[statements.indexOf(node) - 1]
if (prevNode && ts.isVariableStatement(prevNode) && prevNode.declarationList.declarations.length === 1) {
const { name } = prevNode.declarationList.declarations[0]!
if (ts.isIdentifier(name)) {
const kind: ts.ScriptElementKind =
prevNode.declarationList.flags & ts.NodeFlags.Const ? ts.ScriptElementKind.constElement : ts.ScriptElementKind.letElement
entries = boostOrAddSuggestions(entries, [
{
name: name.text,
kind,
},
])
}
}
}
/** false - pick all identifiers after cursor
* node - pick identifiers that within node */
let filterBlock: undefined | false | ts.Node
if (beforeConstNodeOffset !== undefined) {
const node = findChildContainingPosition(ts, sourceFile, position - beforeConstNodeOffset.length - 2)
if (!node || !ts.isVariableDeclarationList(node)) return
if (!node || !ts.isVariableDeclarationList(node)) return entries
filterBlock = false
} else if (ts.isIdentifier(node) && node.parent?.parent) {
// node > parent1 > parent2
Expand All @@ -44,7 +69,7 @@ export default (
}
}

if (filterBlock === undefined) return
if (filterBlock === undefined) return entries
const semanticDiagnostics = languageService.getSemanticDiagnostics(sourceFile.fileName)

const notFoundIdentifiers = semanticDiagnostics
Expand Down
4 changes: 2 additions & 2 deletions typescript/src/completionsAtPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import prepareTextForEmmet from './specialCommands/prepareTextForEmmet'
import switchCaseExcludeCovered from './completions/switchCaseExcludeCovered'
import additionalTypesSuggestions from './completions/additionalTypesSuggestions'
import boostKeywordSuggestions from './completions/boostKeywordSuggestions'
import boostTextSuggestions from './completions/boostNameSuggestions'
import boostNameSuggestions from './completions/boostNameSuggestions'
import keywordsSpace from './completions/keywordsSpace'
import jsdocDefault from './completions/jsdocDefault'
import defaultHelpers from './completions/defaultHelpers'
Expand Down Expand Up @@ -143,7 +143,7 @@ export const getCompletionsAtPosition = (
}

if (leftNode) {
const newEntries = boostTextSuggestions(prior?.entries ?? [], position, sourceFile, leftNode, languageService)
const newEntries = boostNameSuggestions(prior?.entries ?? [], position, sourceFile, leftNode, languageService)
if (newEntries?.length && ensurePrior() && prior) prior.entries = newEntries
}

Expand Down
2 changes: 1 addition & 1 deletion typescript/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default (proxy: ts.LanguageService, languageService: ts.LanguageService,
}
}
}
return
return prior
}

if (__WEB__) {
Expand Down
8 changes: 2 additions & 6 deletions typescript/src/documentHighlights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ export default (proxy: ts.LanguageService, languageService: ts.LanguageService,
proxy.getDocumentHighlights = (fileName, position, filesToSearch) => {
const prior = languageService.getDocumentHighlights(fileName, position, filesToSearch)
if (!prior) return
if (prior.length !== 1) return prior
if (prior.length !== 1 || c('disableUselessHighlighting') === 'disable') return prior
const node = findChildContainingPosition(ts, languageService.getProgram()!.getSourceFile(fileName)!, position)
if (!node) return prior
if (
c('disableUselessHighlighting') !== 'disable' &&
ts.isStringLiteralLike(node) &&
(c('disableUselessHighlighting') === 'inAllStrings' || ts.isJsxAttribute(node.parent))
) {
if (ts.isStringLiteralLike(node) && (c('disableUselessHighlighting') === 'inAllStrings' || ts.isJsxAttribute(node.parent))) {
return
}
return prior
Expand Down
4 changes: 2 additions & 2 deletions typescript/src/getPatchedNavTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ const getPatchedNavModule = (additionalFeatures: AdditionalFeatures): { getNavig
})
const notFoundVariables = new Set<string>()
const cannotFindCodes = getCannotFindCodes({ includeFromLib: false })
for (const { code, messageText } of languageService.getSemanticDiagnostics('main.ts')) {
for (const { code, start, length } of languageService.getSemanticDiagnostics('main.ts')) {
if (!cannotFindCodes.includes(code)) continue
const notFoundName = (typeof messageText === 'object' ? messageText.messageText : messageText).match(/^Cannot find name '(.+?)'./)?.[1]
const notFoundName = moduleString.slice(start, start! + length!)
if (!notFoundName) continue
notFoundVariables.add(notFoundName)
}
Expand Down

0 comments on commit d0b9de8

Please sign in to comment.