Skip to content

Commit

Permalink
Merge pull request #89 from zardoy/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
zardoy authored Jan 31, 2023
2 parents 14e13b0 + bc0d31a commit 936fc4a
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 18 deletions.
46 changes: 41 additions & 5 deletions src/apiCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,53 @@ import { passthroughExposedApiCommands, TriggerCharacterCommand } from '../types
import { sendCommand } from './sendCommand'

export default () => {
const sharedRequest = (type: TriggerCharacterCommand, { offset, relativeOffset = 0 }: RequestOptions) => {
/** @unique */
const cacheableCommands: Set<(typeof passthroughExposedApiCommands)[number]> = new Set(['getNodePath', 'getSpanOfEnclosingComment', 'getNodeAtPosition'])
const operationsCache = new Map<string, { key: string; data }>()
const sharedRequest = async (type: TriggerCharacterCommand, { offset, relativeOffset = 0, document, position }: RequestOptions) => {
if (position && offset) throw new Error('Only position or offset parameter can be provided')
if (document && !offset && !position) throw new Error('When custom document is provided, offset or position must be provided')

const { activeTextEditor } = vscode.window
if (!activeTextEditor) return
const { document, selection } = activeTextEditor
offset ??= document.offsetAt(selection.active) + relativeOffset
return sendCommand(type, { document, position: document.positionAt(offset) })
document ??= activeTextEditor?.document
if (!document) return
if (!position) offset ??= document.offsetAt(activeTextEditor!.selection.active) + relativeOffset
const requestOffset = offset ?? document.offsetAt(position!)
const requestPos = position ?? document.positionAt(offset!)
const getData = async () => sendCommand(type, { document: document!, position: requestPos })
if (cacheableCommands.has(type as any)) {
const cacheEntry = operationsCache.get(type)
const operationKey = `${document.uri.toString()}:${document.version}:${requestOffset}`
if (cacheEntry?.key === operationKey) {
return cacheEntry.data
}

const data = getData()
// intentionally storing data only per one offset because it was created for this specific case:
// extension 1 completion provider requests API data
// at the same time:
// extension 2 completion provider requests API data at the same document and position
// and so on
operationsCache.set(type, { key: operationKey, data })
if (type === 'getNodePath') {
operationsCache.set('getNodeAtPosition', { key: operationKey, data: data.then((path: any) => path[path.length - 1]) })
}

return data
}

return getData()
}

type RequestOptions = Partial<{
/**
* Should be rarely overrided, this document must be part of opened project
* If specificed, offset or position must be provided too
*/
document: vscode.TextDocument
offset: number
relativeOffset: number
position: vscode.Position
}>
for (const cmd of passthroughExposedApiCommands)
vscode.commands.registerCommand(getExtensionCommandId(cmd as never), async (options: RequestOptions = {}) => sharedRequest(cmd, options))
Expand Down
10 changes: 10 additions & 0 deletions src/configurationType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,16 @@ export type Configuration = {
* @default []
*/
additionalIncludeExtensions: string[]
/**
* Patterns to exclude from workspace symbol search
* Example: `["**\/node_modules/**"]`
* Can gradually improve performance, will be set to node_modules by default in future
* @uniqueItems true
* @default []
* @defaultTODO ["**\/node_modules/**"]
*/
// TODO enable node_modules default when cancellationToken is properly used
workspaceSymbolSearchExcludePatterns: string[]
/**
* @default ["fixMissingFunctionDeclaration"]
* @uniqueItems true
Expand Down
2 changes: 2 additions & 0 deletions typescript/src/decorateProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import decorateDocumentHighlights from './documentHighlights'
import completionEntryDetails from './completionEntryDetails'
import { GetConfig } from './types'
import lodashGet from 'lodash.get'
import decorateWorkspaceSymbolSearch from './workspaceSymbolSearch'

/** @internal */
export const thisPluginMarker = '__essentialPluginsMarker__'
Expand Down Expand Up @@ -107,6 +108,7 @@ export const decorateLanguageService = (
decorateDefinitions(proxy, info, c)
decorateReferences(proxy, languageService, c)
decorateDocumentHighlights(proxy, languageService, c)
decorateWorkspaceSymbolSearch(proxy, languageService, c, languageServiceHost)

if (pluginSpecificSyntaxServerConfigCheck) {
if (!__WEB__) {
Expand Down
29 changes: 16 additions & 13 deletions typescript/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,13 @@ export default (proxy: ts.LanguageService, info: ts.server.PluginCreateInfo, c:
}
if (c('miscDefinitionImprovement') && prior.definitions) {
const filterOutReactFcDef = prior.definitions.length === 2
prior.definitions = prior.definitions.filter(({ fileName, containerName, containerKind, kind, name, ...rest }) => {
prior.definitions = prior.definitions.filter(({ fileName, containerName, containerKind, kind, name, textSpan, ...rest }) => {
const isFcDef = filterOutReactFcDef && fileName.endsWith('node_modules/@types/react/index.d.ts') && containerName === 'FunctionComponent'
if (isFcDef) return false
// filter out css modules index definition
if (containerName === 'classes' && containerKind === undefined && rest['isAmbient'] && kind === 'index' && name === '__index') {
// ensure we don't filter out something important?
const nodeAtDefinition = findChildContainingExactPosition(
info.languageService.getProgram()!.getSourceFile(fileName)!,
firstDef.textSpan.start,
)
const nodeAtDefinition = findChildContainingExactPosition(info.languageService.getProgram()!.getSourceFile(fileName)!, textSpan.start)
let moduleDeclaration: ModuleDeclaration | undefined
ts.findAncestor(nodeAtDefinition, node => {
if (ts.isModuleDeclaration(node)) {
Expand All @@ -138,19 +135,25 @@ export default (proxy: ts.LanguageService, info: ts.server.PluginCreateInfo, c:
}
return false
})
if (moduleDeclaration?.name.text === '*.module.css') return false
const cssModules = ['*.module.css', '*.module.scss', '*.module.sass', '*.module.less', '*.module.styl']
if (moduleDeclaration?.name.text && cssModules.includes(moduleDeclaration.name.text)) return false
}
return true
})
}

if (
c('removeModuleFileDefinitions') &&
prior.definitions?.length === 1 &&
firstDef.kind === ts.ScriptElementKind.moduleElement &&
firstDef.name.slice(1, -1).startsWith('*.')
) {
return
if (c('removeModuleFileDefinitions')) {
prior.definitions = prior.definitions?.filter(def => {
if (
def.kind === ts.ScriptElementKind.moduleElement &&
def.name.slice(1, -1).startsWith('*.') &&
def.containerKind === undefined &&
def['isAmbient']
) {
return false
}
return true
})
}

return prior
Expand Down
1 change: 1 addition & 0 deletions typescript/src/specialCommands/handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default (
}
}
if (specialCommand === 'getNodeAtPosition') {
// ensure return data is the same as for node in getNodePath
const node = findChildContainingPosition(ts, sourceFile, position)
return {
entries: [],
Expand Down
33 changes: 33 additions & 0 deletions typescript/src/workspaceSymbolSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { GetConfig } from './types'

export default (proxy: ts.LanguageService, languageService: ts.LanguageService, c: GetConfig, languageServiceHost: ts.LanguageServiceHost) => {
proxy.getNavigateToItems = (searchValue, maxResultCount, fileName, excludeDtsFiles) => {
const workspaceSymbolSearchExcludePatterns = c('workspaceSymbolSearchExcludePatterns')
if (!workspaceSymbolSearchExcludePatterns.length) {
return languageService.getNavigateToItems(searchValue, maxResultCount, fileName, excludeDtsFiles)
}

const program = languageService.getProgram()!
const cancellationToken = languageServiceHost.getCompilerHost?.()?.getCancellationToken?.() ?? {
isCancellationRequested: () => false,
throwIfCancellationRequested: () => {},
}
let sourceFiles = fileName ? [program.getSourceFile(fileName)!] : program.getSourceFiles()
if (!fileName) {
const excludes = tsFull.getRegularExpressionForWildcard(workspaceSymbolSearchExcludePatterns, '', 'exclude')?.slice(1)
if (excludes) {
const re = new RegExp(excludes)
sourceFiles = sourceFiles.filter(x => !re.test(x.fileName))
}
}
return tsFull.NavigateTo.getNavigateToItems(
sourceFiles as any,
program.getTypeChecker() as any,
// TODO! use real cancellationToken
cancellationToken,
searchValue,
maxResultCount,
excludeDtsFiles ?? false,
)
}
}

0 comments on commit 936fc4a

Please sign in to comment.