Skip to content

Commit 200a8e8

Browse files
authored
Merge pull request #44 from typed-ember/language-server
2 parents 646da17 + e2ecc8d commit 200a8e8

22 files changed

+911
-135
lines changed

packages/config/__tests__/include-exclude.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ describe('include/exclude configuration', () => {
1212
expect(config.includesFile(`${root}/deeply/nested/file.ts`)).toBe(true);
1313
});
1414

15+
test('includes all .hbs files within the root', () => {
16+
expect(config.includesFile(`${root}/file.hbs`)).toBe(true);
17+
expect(config.includesFile(`${root}/deeply/nested/file.hbs`)).toBe(true);
18+
});
19+
1520
test('excludes files outside the root', () => {
1621
expect(config.includesFile(`${root}/../other.ts`)).toBe(false);
1722
});
1823

19-
test('excludes non-.ts files', () => {
24+
test('excludes non-.ts/.hbs files', () => {
2025
expect(config.includesFile(`${root}/other.txt`)).toBe(false);
2126
});
2227

packages/config/src/config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ export class GlintConfig {
2525
this.rootDir = normalizePath(rootDir);
2626
this.environment = GlintEnvironment.load(config.environment, { rootDir });
2727

28-
let include = Array.isArray(config.include) ? config.include : [config.include ?? '**/*.ts'];
28+
let include = Array.isArray(config.include)
29+
? config.include
30+
: config.include
31+
? [config.include]
32+
: ['**/*.ts', '**/*.hbs'];
33+
2934
let exclude = Array.isArray(config.exclude)
3035
? config.exclude
3136
: [config.exclude ?? '**/node_modules/**'];

packages/core/__tests__/cli/check.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { stripIndent } from 'common-tags';
22
import stripAnsi from 'strip-ansi';
33
import Project from '../utils/project';
44

5-
describe('single-pass typechecking', () => {
5+
describe('CLI: single-pass typechecking', () => {
66
let project!: Project;
77
beforeEach(async () => {
8+
jest.setTimeout(20_000);
89
project = await Project.create();
910
});
1011

@@ -64,13 +65,13 @@ describe('single-pass typechecking', () => {
6465
expect(checkResult.exitCode).toBe(1);
6566
expect(checkResult.stdout).toEqual('');
6667
expect(stripAnsi(checkResult.stderr)).toMatchInlineSnapshot(`
67-
"index.ts:10:28 - error TS0: [glint] Parse error on line 2:
68+
"index.ts:11:24 - error TS0: Parse error on line 2:
6869
...e to app v{{@version}. The current t
6970
-----------------------^
7071
Expecting 'CLOSE_RAW_BLOCK', 'CLOSE', 'CLOSE_UNESCAPED', 'OPEN_SEXPR', 'CLOSE_SEXPR', 'ID', 'OPEN_BLOCK_PARAMS', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', 'SEP', got 'INVALID'
7172
72-
10 public static template = hbs\`
73-
~~~
73+
11 Welcome to app v{{@version}.
74+
~~~~~~~
7475
"
7576
`);
7677
});

packages/core/__tests__/cli/declaration.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { stripIndent } from 'common-tags';
22
import Project from '../utils/project';
33

4-
describe('emitting declarations', () => {
4+
describe('CLI: emitting declarations', () => {
55
let project!: Project;
66
beforeEach(async () => {
77
project = await Project.create();

packages/core/__tests__/cli/watch.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import stripAnsi from 'strip-ansi';
33
import os from 'os';
44
import Project from '../utils/project';
55

6-
describe('watched typechecking', () => {
6+
describe('CLI: watched typechecking', () => {
77
let project!: Project;
88
beforeEach(async () => {
99
jest.setTimeout(20_000);
@@ -75,13 +75,13 @@ describe('watched typechecking', () => {
7575

7676
expect(output).toMatch('Found 1 error.');
7777
expect(error.replace(/\r/g, '')).toMatchInlineSnapshot(`
78-
"index.ts:10:28 - error TS0: [glint] Parse error on line 2:
78+
"index.ts:11:24 - error TS0: Parse error on line 2:
7979
...e to app v{{@version}. The current t
8080
-----------------------^
8181
Expecting 'CLOSE_RAW_BLOCK', 'CLOSE', 'CLOSE_UNESCAPED', 'OPEN_SEXPR', 'CLOSE_SEXPR', 'ID', 'OPEN_BLOCK_PARAMS', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', 'SEP', got 'INVALID'
8282
83-
10 public static template = hbs\`
84-
~~~"
83+
11 Welcome to app v{{@version}.
84+
~~~~~~~"
8585
`);
8686
});
8787

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env node
2+
/* eslint-disable */
3+
require('../lib/language-server');

packages/core/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"bin"
1212
],
1313
"bin": {
14-
"glint": "bin/glint.js"
14+
"glint": "bin/glint.js",
15+
"glint-language-server": "bin/glint-language-server.js"
1516
},
1617
"scripts": {
1718
"lint": "eslint . --max-warnings 0 && prettier --check src",
@@ -23,6 +24,9 @@
2324
"@glint/config": "^0.2.1",
2425
"@glint/transform": "^0.2.1",
2526
"resolve": "^1.17.0",
27+
"vscode-languageserver": "^7.0.0",
28+
"vscode-languageserver-textdocument": "^1.0.1",
29+
"vscode-uri": "^3.0.2",
2630
"yargs": "^15.3.1"
2731
},
2832
"devDependencies": {

packages/core/src/cli/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { loadConfig } from '@glint/config';
33
import { performWatch } from './perform-watch';
44
import { performCheck } from './perform-check';
55
import { determineOptionsToExtend } from './options';
6-
import { loadTypeScript } from './load-typescript';
6+
import { loadTypeScript } from '../common/load-typescript';
77

88
const { argv } = yargs
99
.scriptName('glint')

packages/core/src/cli/perform-check.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type ts from 'typescript';
2-
import TransformManager from './transform-manager';
2+
import TransformManager from '../common/transform-manager';
33
import { GlintConfig } from '@glint/config';
44

55
export function performCheck(
@@ -46,7 +46,7 @@ function createCompilerHost(
4646
transformManager: TransformManager
4747
): ts.CompilerHost {
4848
let host = ts.createCompilerHost(options);
49-
host.readFile = transformManager.readFile;
49+
host.readFile = transformManager.readTransformedFile;
5050
return host;
5151
}
5252

packages/core/src/cli/perform-watch.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import TransformManager from './transform-manager';
1+
import TransformManager from '../common/transform-manager';
22
import { GlintConfig } from '@glint/config';
33

44
export function performWatch(
@@ -27,8 +27,8 @@ function sysForWatchCompilerHost(
2727
): typeof ts.sys {
2828
return {
2929
...ts.sys,
30-
watchFile: transformManager.watchFile,
31-
readFile: transformManager.readFile,
30+
watchFile: transformManager.watchTransformedFile,
31+
readFile: transformManager.readTransformedFile,
3232
};
3333
}
3434

@@ -47,7 +47,7 @@ function patchProgram(program: Program, transformManager: TransformManager): voi
4747
let { getSyntacticDiagnostics } = program;
4848
program.getSyntacticDiagnostics = function (sourceFile, cancelationToken) {
4949
let diagnostics = getSyntacticDiagnostics.call(program, sourceFile, cancelationToken);
50-
let transformDiagnostics = transformManager.getTransformDiagnostics(sourceFile);
50+
let transformDiagnostics = transformManager.getTransformDiagnostics(sourceFile?.fileName);
5151
return [...diagnostics, ...transformDiagnostics];
5252
};
5353
}

packages/core/src/cli/transform-manager.ts

Lines changed: 0 additions & 113 deletions
This file was deleted.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { GlintConfig } from '@glint/config';
2+
3+
export type Document = {
4+
version: number;
5+
contents: string;
6+
stale: boolean;
7+
};
8+
9+
/**
10+
* A read-through cache for workspace document contents.
11+
*
12+
* This cache is aware (via the glint configuration it's instantiated with)
13+
* of the relationship between certain `.ts` and `.hbs` files, and will
14+
* treat a change to one member of such pairs as affecting the version
15+
* of both.
16+
*/
17+
export default class DocumentCache {
18+
private readonly documents = new Map<string, Document>();
19+
20+
public constructor(private ts: typeof import('typescript'), private glintConfig: GlintConfig) {}
21+
22+
public documentExists(path: string): boolean {
23+
return this.documents.has(path) || this.ts.sys.fileExists(path);
24+
}
25+
26+
public getCompanionDocumentPath(path: string): string | undefined {
27+
let { environment } = this.glintConfig;
28+
let candidates = isTemplate(path)
29+
? environment.getPossibleScriptPaths(path)
30+
: environment.getPossibleTemplatePaths(path);
31+
32+
for (let candidate of candidates) {
33+
if (this.documentExists(candidate)) {
34+
return candidate;
35+
}
36+
}
37+
}
38+
39+
public getDocumentContents(path: string, encoding?: string): string {
40+
if (!this.documentExists(path)) return '';
41+
42+
let document = this.getDocument(path);
43+
if (document.stale) {
44+
document.contents = this.ts.sys.readFile(path, encoding) ?? '';
45+
document.stale = false;
46+
}
47+
48+
return document.contents;
49+
}
50+
51+
public getCompoundDocumentVersion(path: string): string {
52+
let template = isTemplate(path) ? this.getDocument(path) : this.findCompanionDocument(path);
53+
let script = isTemplate(path) ? this.findCompanionDocument(path) : this.getDocument(path);
54+
55+
return `${script?.version}:${template?.version}`;
56+
}
57+
58+
public getDocumentVersion(path: string): string {
59+
return this.getDocument(path).version.toString();
60+
}
61+
62+
public updateDocument(path: string, contents: string): void {
63+
let document = this.getDocument(path);
64+
65+
document.stale = false;
66+
document.contents = contents;
67+
document.version++;
68+
69+
this.incrementCompanionVersion(path);
70+
}
71+
72+
public markDocumentStale(path: string): void {
73+
let document = this.getDocument(path);
74+
75+
document.stale = true;
76+
document.version++;
77+
78+
this.incrementCompanionVersion(path);
79+
}
80+
81+
public removeDocument(path: string): void {
82+
this.documents.delete(path);
83+
}
84+
85+
private incrementCompanionVersion(path: string): void {
86+
let companion = this.findCompanionDocument(path);
87+
if (companion) {
88+
companion.version++;
89+
}
90+
}
91+
92+
private findCompanionDocument(path: string): Document | undefined {
93+
let companionPath = this.getCompanionDocumentPath(path);
94+
return companionPath ? this.getDocument(companionPath) : undefined;
95+
}
96+
97+
private getDocument(path: string): Document {
98+
let document = this.documents.get(path);
99+
if (!document) {
100+
document = { version: 0, contents: '', stale: true };
101+
this.documents.set(path, document);
102+
}
103+
return document;
104+
}
105+
}
106+
107+
export function isTemplate(path: string): boolean {
108+
return path.endsWith('.hbs');
109+
}
110+
111+
export function isScript(path: string): boolean {
112+
return path.endsWith('.ts');
113+
}

0 commit comments

Comments
 (0)