Skip to content

Commit

Permalink
Consider open unattached scriptInfo for external files (#303)
Browse files Browse the repository at this point in the history
  • Loading branch information
znck authored Oct 18, 2022
1 parent 9c20060 commit 491fe1f
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 121 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"typecheck",
"ucfirst",
"Uncapitalize",
"unconfigured",
"vetur",
"vuedx"
],
Expand Down
83 changes: 66 additions & 17 deletions packages/typescript-plugin-vue/src/managers/PluginManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { first, setDebugging } from '@vuedx/shared'
import { createHash } from 'crypto'
import { Container } from 'inversify'
import { TS_LANGUAGE_SERVICE } from '../constants'
import type {
Expand Down Expand Up @@ -28,24 +27,33 @@ export class PluginManager {
readonly #containers = new Map<string, Container>()
private readonly logger = LoggerService.getLogger(PluginManager.name)

private _activeContainerId: string | undefined
public create(options: Options): TSLanguageService {
this.#setupLogger(options)

if (TS_LANGUAGE_SERVICE in options.languageService) {
return options.languageService
}

this.#patchTypescript(options.typescript)

const containerKey = options.project.getProjectName()

const container =
this.#containers.get(containerKey) ?? this.#createContainer(options)

if (this._activeContainerId === containerKey) {
return this.#createLanguageService(
options.languageService,
container.get(TypescriptPluginService),
)
}

this.logger.debug(
'Creating language service for project:',
options.project.getProjectName(),
)

this.#patchTypescript(options.typescript)

const container =
this.#containers.get(options.project.getProjectName()) ??
this.#createContainer(options)

container.get(TypescriptContextService).updateOptions(options)

this.#patchProject(container, options.project)
Expand All @@ -54,8 +62,13 @@ export class PluginManager {

try {
const plugin = container.get(TypescriptPluginService)
plugin.onDispose(() => {
this.#containers.delete(containerKey)
container.unbindAll()
})
return this.#createLanguageService(options.languageService, plugin)
} finally {
this._activeContainerId = containerKey
const current = (
(options.project.projectService as any)
.hostConfiguration as TypeScript.server.HostConfiguration
Expand All @@ -72,6 +85,7 @@ export class PluginManager {
extraFileExtensions: [],
})
}
this._activeContainerId = undefined
}
}

Expand Down Expand Up @@ -120,6 +134,7 @@ export class PluginManager {

#patchProject(container: Container, project: TSProject): void {
const ts = container.get(TypescriptContextService)
const fs = container.get(FilesystemService)
const logger = LoggerService.getLogger('Project')

overrideMethod(
Expand Down Expand Up @@ -179,6 +194,35 @@ export class PluginManager {
}
},
)

overrideMethod(
project as unknown as {
detachScriptInfoFromProject(
uncheckedFileName: string,
noRemoveResolution?: boolean,
): void
},
'detachScriptInfoFromProject',
(detachScriptInfoFromProject) =>
(uncheckedFileName, noRemoveResolution) => {
if (fs.isVueFile(uncheckedFileName)) return
if (fs.isGeneratedVueFile(uncheckedFileName)) {
const fileName = fs.getRealFileNameIfAny(uncheckedFileName)
console.debug(`@@@ Detaching ${fileName}`)
return detachScriptInfoFromProject.call(
project,
fileName,
noRemoveResolution,
)
}

return detachScriptInfoFromProject.call(
project,
uncheckedFileName,
noRemoveResolution,
)
},
)
}

#patchServerHost(container: Container, serverHost: TSServerHost): void {
Expand Down Expand Up @@ -311,7 +355,8 @@ export class PluginManager {
containingFile,
reusedNames,
redirectedReference,
_options,
options,
containingSourceFile,
) => {
if (fs.isVueRuntimeFile(containingFile)) {
const anyProjectFile = first(ts.project.getRootFiles())
Expand All @@ -320,15 +365,15 @@ export class PluginManager {
const core = ts.lib.resolveModuleName(
'@vue/runtime-core',
anyProjectFile,
_options,
options,
ts.serverHost,
undefined,
redirectedReference,
)
const vue = ts.lib.resolveModuleName(
'vue',
anyProjectFile,
_options,
options,
ts.serverHost,
undefined,
redirectedReference,
Expand All @@ -352,13 +397,15 @@ export class PluginManager {
containingFile,
reusedNames,
redirectedReference,
_options,
options,
)
: ts.project.resolveModuleNames(
moduleNames,
containingFile,
reusedNames,
redirectedReference,
options,
containingSourceFile,
)

const known = {
Expand All @@ -373,7 +420,8 @@ export class PluginManager {
}
moduleNames.forEach((name, index) => {
const handler = known[name as keyof typeof known]
if (handler != null && result[index] == null) {
const resolved = result[index]
if (handler != null && resolved == null) {
result[index] = handler()
}
})
Expand Down Expand Up @@ -409,11 +457,13 @@ export class PluginManager {
return container
}

readonly #loggerIds = new Map<string, string>()
#setupLogger(options: Options): void {
const id = createHash('md5')
.update(options.project.getProjectName())
.digest('hex')
.slice(0, 6)
const id =
this.#loggerIds.get(options.project.getProjectName()) ??
`${this.#loggerIds.size}`

this.#loggerIds.set(options.project.getProjectName(), id)

if (LoggerService.currentId === id) return
const logger = options.project.projectService.logger
Expand Down Expand Up @@ -464,7 +514,6 @@ export class PluginManager {
has: (target, prop) => {
return prop === TS_LANGUAGE_SERVICE || prop in target
},
// TODO: Implement set?
})
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,7 @@ export class TypescriptPluginService
private readonly ipc: IPCService,
) {
if (Math.random() > 1) {
console.log([
this.classifications,
this.codeFix,
this.completions,
this.definitions,
this.folding,
this.implementation,
this.quickInfo,
this.refactor,
this.references,
this.rename,
this.signature,
])
console.log([this.classifications, this.folding, this.implementation])
}
}
//#endregion
Expand Down Expand Up @@ -139,6 +127,20 @@ export class TypescriptPluginService
const vue = new Set<string>()
const virtual = new Set<string>()

// This is not needed for any functionality, but it's needed to prevent unnecessary creation of inferred project.
this.ts.projectService.openFiles.forEach((_, file) => {
const scriptInfo = this.ts.projectService.getScriptInfoForPath(
file as TypeScript.Path,
)
if (scriptInfo == null) return
if (
scriptInfo.containingProjects.length === 0 || // creating new project, so this is likely to be part of current project. TODO: verify hypothesis.
scriptInfo.containingProjects.includes(this.ts.project)
) {
all.push(scriptInfo.fileName)
}
})

if (this.ts.isConfiguredProject(this.ts.project)) {
const options = this.ts.project.getParsedCommandLine?.(
this.ts.project.getConfigFilePath(),
Expand All @@ -161,23 +163,15 @@ export class TypescriptPluginService

if (vue.size === 0) {
this.#isVueProject = false
this.logger.debug('Not a Vue project')
this.logger.debug('Not a Vue project:', this.ts.project.getProjectName())
return [] // do not retain any files if no .vue files
}

this.#isVueProject = true
const fileNames = [...this.getScriptFileNames([...vue]), ...virtual]

this.logger.debug(`Project: ${this.ts.project.getProjectName()}`)
this.logger.debug(`Project:`, this.ts.project.getProjectName())
this.logger.debug(`External files:`, fileNames)
this.logger.debug(
'Open external files:',
fileNames.filter((fileName) =>
this.ts.projectService.openFiles.has(
this.ts.toNormalizedPath(fileName),
),
),
)

return fileNames
}
Expand Down Expand Up @@ -958,8 +952,15 @@ export class TypescriptPluginService
return this.ts.service.getTodoComments(fileName, descriptors)
}

readonly #disposables: Array<() => void> = []

public onDispose(callback: () => void): void {
this.#disposables.push(callback)
}

public dispose(): void {
this.ipc.dispose()
this.#disposables.forEach((dispose) => dispose())
this.ts.service.dispose()
}

Expand Down
Empty file.
Empty file.
Loading

0 comments on commit 491fe1f

Please sign in to comment.