Skip to content

Commit 3e82359

Browse files
authored
Use parsed document to determine template type (#222)
1 parent 056d922 commit 3e82359

File tree

6 files changed

+237
-335
lines changed

6 files changed

+237
-335
lines changed

src/document/CloudFormationDetection.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/document/Document.ts

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TextDocument, Position, Range, DocumentUri } from 'vscode-languageserver-textdocument';
2+
import { TopLevelSection } from '../context/ContextType';
23
import { DefaultSettings } from '../settings/Settings';
34
import { LoggerFactory } from '../telemetry/LoggerFactory';
4-
import { detectCfnFileType } from './CloudFormationDetection';
55
import { DocumentMetadata } from './DocumentProtocol';
66
import { detectDocumentType, uriToPath } from './DocumentUtils';
77
import { parseValidYaml } from './YamlParser';
@@ -10,9 +10,10 @@ export class Document {
1010
private readonly log = LoggerFactory.getLogger(Document);
1111
public readonly extension: string;
1212
public readonly documentType: DocumentType;
13-
public readonly cfnFileType: CloudFormationFileType;
13+
private _cfnFileType: CloudFormationFileType;
1414
public readonly fileName: string;
1515
private tabSize: number;
16+
private cachedParsedContent: unknown;
1617

1718
constructor(
1819
private readonly textDocument: TextDocument,
@@ -28,22 +29,87 @@ export class Document {
2829
this.extension = extension;
2930
this.documentType = type;
3031
this.fileName = uriToPath(uri).base;
32+
this._cfnFileType = CloudFormationFileType.Unknown;
3133

32-
try {
33-
this.cfnFileType = detectCfnFileType(this.textDocument.getText(), this.documentType);
34-
} catch (error) {
35-
this.cfnFileType = CloudFormationFileType.Unknown;
36-
this.log.error(error, `Failed to detect CloudFormation file type ${this.textDocument.uri}`);
37-
}
34+
this.updateCfnFileType();
3835
this.tabSize = fallbackTabSize;
3936
this.processIndentation(detectIndentation, fallbackTabSize);
4037
}
4138

42-
public getParsedDocumentContent(): unknown {
39+
public get cfnFileType(): CloudFormationFileType {
40+
return this._cfnFileType;
41+
}
42+
43+
public updateCfnFileType(): void {
44+
const content = this.textDocument.getText();
45+
if (!content.trim()) {
46+
this._cfnFileType = CloudFormationFileType.Unknown;
47+
this.cachedParsedContent = undefined;
48+
return;
49+
}
50+
51+
try {
52+
this.cachedParsedContent = this.parseContent();
53+
this._cfnFileType = this.detectCfnFileType();
54+
} catch {
55+
// If parsing fails, leave cfnFileType unchanged and clear cache
56+
this.cachedParsedContent = undefined;
57+
this.log.debug(
58+
`Failed to parse document ${this.textDocument.uri}, keeping cfnFileType as ${this._cfnFileType}`,
59+
);
60+
}
61+
}
62+
63+
private parseContent(): unknown {
64+
const content = this.textDocument.getText();
4365
if (this.documentType === DocumentType.JSON) {
44-
return JSON.parse(this.contents());
66+
return JSON.parse(content);
67+
}
68+
return parseValidYaml(content);
69+
}
70+
71+
private detectCfnFileType(): CloudFormationFileType {
72+
// If languageId is cloudformation, treat as template
73+
if (this.languageId === 'cloudformation') {
74+
return CloudFormationFileType.Template;
75+
}
76+
77+
if (!this.cachedParsedContent || typeof this.cachedParsedContent !== 'object') {
78+
return CloudFormationFileType.Unknown;
79+
}
80+
81+
const parsed = this.cachedParsedContent as Record<string, unknown>;
82+
83+
// Check for GitSync deployment file
84+
const gitSyncKeys = ['template-file-path', 'templateFilePath', 'templatePath'];
85+
if (gitSyncKeys.some((key) => Object.prototype.hasOwnProperty.call(parsed, key))) {
86+
return CloudFormationFileType.GitSyncDeployment;
87+
}
88+
89+
// Check for CloudFormation template
90+
const templateKeys = [
91+
TopLevelSection.AWSTemplateFormatVersion,
92+
TopLevelSection.Resources,
93+
TopLevelSection.Transform,
94+
];
95+
if (templateKeys.some((key) => Object.prototype.hasOwnProperty.call(parsed, key))) {
96+
return CloudFormationFileType.Template;
97+
}
98+
99+
return CloudFormationFileType.Unknown;
100+
}
101+
102+
public getParsedDocumentContent(): unknown {
103+
if (this.cachedParsedContent !== undefined) {
104+
return this.cachedParsedContent;
105+
}
106+
107+
// Fallback to parsing if cache is empty
108+
try {
109+
return this.parseContent();
110+
} catch {
111+
return undefined;
45112
}
46-
return parseValidYaml(this.contents());
47113
}
48114

49115
public getLine(lineNumber: number): string | undefined {

tools/utils.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { readFileSync, statSync } from 'fs';
2+
import { TextDocument } from 'vscode-languageserver-textdocument';
23
import { detectDocumentType, uriToPath } from '../src/document/DocumentUtils';
3-
import { CloudFormationFileType, DocumentType } from '../src/document/Document';
4-
import { detectCfnFileType } from '../src/document/CloudFormationDetection';
4+
import { CloudFormationFileType, DocumentType, Document } from '../src/document/Document';
55

66
export type TestPosition = {
77
line: number;
@@ -173,13 +173,15 @@ export function discoverTemplateFiles(paths: string[]): TemplateFile[] {
173173
.map((path): TemplateFile => {
174174
const content = readFileSync(path, 'utf8');
175175
const { extension, type } = detectDocumentType(path, content);
176+
const textDocument = TextDocument.create(path, type === DocumentType.JSON ? 'json' : 'yaml', 1, content);
177+
const document = new Document(textDocument);
176178

177179
return {
178180
name: uriToPath(path).base,
179181
path: path,
180182
extension,
181183
documentType: type,
182-
cfnFileType: detectCfnFileType(content, type),
184+
cfnFileType: document.cfnFileType,
183185
content,
184186
size: statSync(path).size,
185187
};

0 commit comments

Comments
 (0)