Skip to content

Commit

Permalink
Merge pull request #9 from lydell/fix-toString-error
Browse files Browse the repository at this point in the history
Use Map instead of Record to avoid the toString problem
  • Loading branch information
ryan-haskell authored Jul 24, 2023
2 parents c80b8ac + 550b130 commit e2d2e19
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 98 deletions.
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function activate(context: vscode.ExtensionContext) {
let globalState: GlobalState = {
isFirstTimeRunningPlugin: true,
elmJsonFiles: [],
cachedDocs: {},
cachedDocs: new Map(),
jumpToDocDetails: undefined
}
context.subscriptions.push({
Expand Down
32 changes: 18 additions & 14 deletions src/features/jump-to-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,13 @@ const provider = (globalState: GlobalState) => {
}

type HandleJumpToLinksForImportsInput = {
document: vscode.TextDocument
position: vscode.Position
ast: ElmSyntax.Ast
elmJsonFile: ElmJsonFile
packages: Packages
}

const handleJumpToLinksForImports =
async ({ document, position, ast, elmJsonFile, packages }: HandleJumpToLinksForImportsInput)
async ({ position, ast, elmJsonFile }: HandleJumpToLinksForImportsInput)
: Promise<vscode.Location | null> => {

for (let import_ of ast.imports) {
Expand Down Expand Up @@ -128,18 +126,17 @@ const provider = (globalState: GlobalState) => {
ast: ElmSyntax.Ast,
doc: vscode.TextDocument
elmJsonFile: ElmJsonFile
packages: Packages
}

const handleJumpToLinksForDeclarations = async ({ position, ast, doc, elmJsonFile, packages }: HandleJumpToLinksForDeclarationsInput): Promise<vscode.Location | null> => {
const handleJumpToLinksForDeclarations = async ({ position, ast, doc, elmJsonFile }: HandleJumpToLinksForDeclarationsInput): Promise<vscode.Location | null> => {
let {
aliasMappingToModuleNames,
explicitExposingValuesForImports,
hasUnknownImportsFromExposingAll
} = ElmSyntax.getInitialPreludeMappings()

const findImportedModuleNamesThatMightHaveExposedThisValue = (moduleName: string): string[] => {
let explicitMatches = explicitExposingValuesForImports[moduleName] || []
let explicitMatches = explicitExposingValuesForImports.get(moduleName) ?? []
return explicitMatches.concat(hasUnknownImportsFromExposingAll)
}

Expand Down Expand Up @@ -315,7 +312,7 @@ const provider = (globalState: GlobalState) => {
// would return "Html.Attributes"
let parentModuleName = parentModules.join('.')

let aliases = aliasMappingToModuleNames[parentModuleName] || []
let aliases = aliasMappingToModuleNames.get(parentModuleName) ?? []
let moduleNamesToCheck = [parentModuleName].concat(aliases)

// Check local project files
Expand Down Expand Up @@ -892,8 +889,12 @@ const provider = (globalState: GlobalState) => {
if (import_.value.moduleAlias) {
let alias = import_.value.moduleAlias.value[0]
if (alias !== undefined) {
aliasMappingToModuleNames[alias] = aliasMappingToModuleNames[alias] || [] as string[]
(aliasMappingToModuleNames[alias] as any).push(moduleName)
const previous = aliasMappingToModuleNames.get(alias)
if (previous === undefined) {
aliasMappingToModuleNames.set(alias, [moduleName])
} else {
previous.push(moduleName)
}
}
}

Expand All @@ -910,8 +911,12 @@ const provider = (globalState: GlobalState) => {
.map(node => ElmSyntax.toTopLevelExposeName(node.value))

for (let exportedName of namesOfExportedThings) {
explicitExposingValuesForImports[exportedName] = explicitExposingValuesForImports[exportedName] || [] as string[]
(explicitExposingValuesForImports[exportedName] as string[]).push(moduleName)
const previous = explicitExposingValuesForImports.get(exportedName)
if (previous === undefined) {
explicitExposingValuesForImports.set(exportedName, [moduleName])
} else {
previous.push(moduleName)
}
}

if (isExposingAnyCustomVariants) {
Expand Down Expand Up @@ -1014,15 +1019,14 @@ const provider = (globalState: GlobalState) => {
}

// Handle module imports
let packages = await sharedLogic.getMappingOfModuleNameToDocJsonFilepath(globalState, elmJsonFile)
matchingLocation = await handleJumpToLinksForImports({ document: doc, position, ast, elmJsonFile, packages })
matchingLocation = await handleJumpToLinksForImports({ position, ast, elmJsonFile })
if (matchingLocation) {
console.info('provideDefinition', `${Date.now() - start}ms`)
return matchingLocation
}

// Handle module declarations
matchingLocation = await handleJumpToLinksForDeclarations({ position, ast, doc, elmJsonFile, packages })
matchingLocation = await handleJumpToLinksForDeclarations({ position, ast, doc, elmJsonFile })
if (matchingLocation) {
console.info('provideDefinition', `${Date.now() - start}ms`)
return matchingLocation
Expand Down
12 changes: 6 additions & 6 deletions src/features/offline-package-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export const feature: Feature = ({ globalState, context }) => {
let moduleNameNode = importNode.value.moduleName
let range = SharedLogic.fromElmRange(moduleNameNode.range)
let moduleName = moduleNameNode.value.join(".")
let docsJsonFsPath = packages[moduleName]
if (docsJsonFsPath) {
let docsJsonFsPath = packages.get(moduleName)
if (docsJsonFsPath !== undefined) {
details.push({
range,
docsJsonFsPath,
Expand Down Expand Up @@ -258,8 +258,8 @@ export const feature: Feature = ({ globalState, context }) => {
)

for (let moduleName of moduleNames) {
let docsJsonFsPath = packages[moduleName]
if (docsJsonFsPath) {
let docsJsonFsPath = packages.get(moduleName)
if (docsJsonFsPath !== undefined) {
let typeOrValueName = await SharedLogic.doesModuleExposesValue(
globalState,
foo,
Expand Down Expand Up @@ -343,8 +343,8 @@ export const feature: Feature = ({ globalState, context }) => {
)

for (let moduleName of moduleNames) {
let docsJsonFsPath = packages[moduleName]
if (docsJsonFsPath) {
let docsJsonFsPath = packages.get(moduleName)
if (docsJsonFsPath !== undefined) {
let typeOrValueName = await SharedLogic.doesModuleExposesValue(
globalState,
foo,
Expand Down
4 changes: 2 additions & 2 deletions src/features/shared/autodetect-elm-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sharedLogic from './logic'
export type GlobalState = {
isFirstTimeRunningPlugin: boolean
elmJsonFiles: ElmJsonFile[]
cachedDocs: Record<string, ModuleDoc[]>
cachedDocs: Map<string, ModuleDoc[]>
jumpToDocDetails: JumpToDocDetails[] | undefined
}

Expand Down Expand Up @@ -51,7 +51,7 @@ export const run = async (globalState: GlobalState) => {
let elmJsonFileUris = await vscode.workspace.findFiles('**/elm.json', '**/node_modules/**', 10)
let possibleElmJsonFiles = await Promise.all(toElmJsonFiles({ elmJsonFileUris, settings }))
globalState.elmJsonFiles = possibleElmJsonFiles.filter(sharedLogic.isDefined)
globalState.cachedDocs = {}
globalState.cachedDocs = new Map()
console.info(`autodetectElmJson`, `${Date.now() - start}ms`)
}

Expand Down
8 changes: 4 additions & 4 deletions src/features/shared/elm-json-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,18 @@ export type BinOp = {


export const getDocumentationForElmPackage = async (globalState: GlobalState, fsPath: string): Promise<ModuleDoc[]> => {
let cachedDocsForThisFsPath = globalState.cachedDocs[fsPath]
let cachedDocsForThisFsPath = globalState.cachedDocs.get(fsPath)

if (cachedDocsForThisFsPath) {
if (cachedDocsForThisFsPath !== undefined) {
return cachedDocsForThisFsPath
} else {
try {
let buffer = await vscode.workspace.fs.readFile(vscode.Uri.file(fsPath))
let contents = Buffer.from(buffer).toString('utf8')
let json = JSON.parse(contents)
globalState.cachedDocs[fsPath] = json
globalState.cachedDocs.set(fsPath, json)
return json
} catch (_) {
} catch {
return []
}
}
Expand Down
35 changes: 23 additions & 12 deletions src/features/shared/elm-to-ast/elm-syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,9 @@ export type ModuleImportTracker = {
findImportedModuleNamesForQualifiedValue: (moduleName: string) => string[]
}

const toMap = <V>(record: Record<string, V>): Map<string, V> =>
new Map(Object.entries(record))

// Need to build up a collection of which types and values
// are being exposed by all imports.
// (This will be useful later when jumping to definitions)
Expand All @@ -393,12 +396,12 @@ type ImportAlias = string
type ExposedValue = string
type ModuleName = string
type InitialPreludeData = {
explicitExposingValuesForImports: Record<ExposedValue, ModuleName[]>
explicitExposingValuesForImports: Map<ExposedValue, ModuleName[]>
hasUnknownImportsFromExposingAll: ModuleName[]
aliasMappingToModuleNames: Record<ImportAlias, ModuleName[]>
aliasMappingToModuleNames: Map<ImportAlias, ModuleName[]>
}
export let getInitialPreludeMappings = (): InitialPreludeData => ({
explicitExposingValuesForImports: {
explicitExposingValuesForImports: toMap({
'List': ['List'],
'(::)': ['List'],
'Maybe': ['Maybe'],
Expand All @@ -412,12 +415,12 @@ export let getInitialPreludeMappings = (): InitialPreludeData => ({
'Program': ['Platform'],
'Cmd': ['Platform.Cmd'],
'Sub': ['Platform.Sub'],
},
}),
hasUnknownImportsFromExposingAll: ['Basics'],
aliasMappingToModuleNames: {
aliasMappingToModuleNames: toMap({
'Cmd': ['Platform.Cmd'],
'Sub': ['Platform.Sub']
}
})
})

export const createModuleImportTracker = (ast: Ast): ModuleImportTracker => {
Expand All @@ -435,8 +438,12 @@ export const createModuleImportTracker = (ast: Ast): ModuleImportTracker => {
if (import_.value.moduleAlias) {
let alias = import_.value.moduleAlias.value[0]
if (alias !== undefined) {
aliasMappingToModuleNames[alias] = aliasMappingToModuleNames[alias] || [] as string[]
(aliasMappingToModuleNames[alias] as any).push(moduleName)
const previous = aliasMappingToModuleNames.get(alias)
if (previous === undefined) {
aliasMappingToModuleNames.set(alias, [moduleName])
} else {
previous.push(moduleName)
}
}
}

Expand All @@ -452,8 +459,12 @@ export const createModuleImportTracker = (ast: Ast): ModuleImportTracker => {
.map(node => toTopLevelExposeName(node.value))

for (let exportedName of namesOfExportedThings) {
explicitExposingValuesForImports[exportedName] = explicitExposingValuesForImports[exportedName] || [] as string[]
(explicitExposingValuesForImports[exportedName] as string[]).push(moduleName)
const previous = explicitExposingValuesForImports.get(exportedName)
if (previous === undefined) {
explicitExposingValuesForImports.set(exportedName, [moduleName])
} else {
previous.push(moduleName)
}
}

if (isExposingAnyCustomVariants) {
Expand All @@ -467,11 +478,11 @@ export const createModuleImportTracker = (ast: Ast): ModuleImportTracker => {

return {
findImportedModuleNamesThatMightHaveExposedThisValue: (typeOrValueName: string): string[] => {
let explicitMatches = explicitExposingValuesForImports[typeOrValueName] || []
let explicitMatches = explicitExposingValuesForImports.get(typeOrValueName) ?? []
return explicitMatches.concat(hasUnknownImportsFromExposingAll)
},
findImportedModuleNamesForQualifiedValue: (moduleName: string): string[] => {
let aliases = aliasMappingToModuleNames[moduleName] || []
let aliases = aliasMappingToModuleNames.get(moduleName) ?? []
let moduleNamesToCheck = [moduleName].concat(aliases)
return moduleNamesToCheck
}
Expand Down
16 changes: 8 additions & 8 deletions src/features/shared/logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ let findElmJsonFor = (globalState: AutodetectElmJson.GlobalState, uri: vscode.Ur
}
}

const getMappingOfModuleNameToDocJsonFilepath = async (globalState: AutodetectElmJson.GlobalState, elmJsonFile: ElmJsonFile): Promise<Record<string, string>> => {
let packages: { [key: string]: string } = {}
const getMappingOfModuleNameToDocJsonFilepath = async (globalState: AutodetectElmJson.GlobalState, elmJsonFile: ElmJsonFile): Promise<Map<string, string>> => {
const packages = new Map<string, string>()
const dependencies = elmJsonFile.dependencies
for (let dep of dependencies) {
let docs = await getDocumentationForElmPackage(globalState, dep.fsPath)
for (let doc of docs) {
packages[doc.name] = dep.fsPath
for (const dep of dependencies) {
const docs = await getDocumentationForElmPackage(globalState, dep.fsPath)
for (const doc of docs) {
packages.set(doc.name, dep.fsPath)
}
}

return packages
}

const findFirstOccurenceOfWordInFile = (word: string, rawJsonString: string): [number, number, number, number] | undefined => {
const findFirstOccurrenceOfWordInFile = (word: string, rawJsonString: string): [number, number, number, number] | undefined => {
if (word && rawJsonString) {
const regex = new RegExp(word, 'm')
const match = rawJsonString.match(regex)
Expand Down Expand Up @@ -104,7 +104,7 @@ export default {
findElmJsonFor,
fromElmRange,
getMappingOfModuleNameToDocJsonFilepath,
findFirstOccurenceOfWordInFile,
findFirstOccurenceOfWordInFile: findFirstOccurrenceOfWordInFile,
isDefined,
doesModuleExposesValue,
keepFilesThatExist
Expand Down
Loading

0 comments on commit e2d2e19

Please sign in to comment.