Skip to content

Commit

Permalink
Merge pull request #51 from typed-ember/vscode-extension
Browse files Browse the repository at this point in the history
  • Loading branch information
dfreeman authored Mar 1, 2021
2 parents 2e6eee8 + 5a10c97 commit e06775b
Show file tree
Hide file tree
Showing 26 changed files with 647 additions and 193 deletions.
70 changes: 44 additions & 26 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,33 +1,51 @@
{
"root": true,
"plugins": ["@typescript-eslint"],
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"extends": ["eslint:recommended"],
"parserOptions": {
"ecmaVersion": 2020
},
"rules": {
"prefer-const": "off",
"require-yield": "off",
"@typescript-eslint/prefer-const": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/explicit-function-return-type": [
"error",
{
"allowExpressions": true
"require-yield": "off"
},
"overrides": [
{
"files": "*.js",
"env": {
"node": true
}
],
},
{
"files": "*.ts",
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"prefer-const": "off",
"require-yield": "off",
"@typescript-eslint/prefer-const": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/explicit-function-return-type": [
"error",
{
"allowExpressions": true
}
],

// Because of the number of type constraints we deal with that need to be
// able to appear in both covariant and contravariant positions, it becomes
// incredibly unwieldy to manage variants of each constraint with `never`
// and `unknown` in all the right places
"@typescript-eslint/no-explicit-any": "off"
}
// Because of the number of type constraints we deal with that need to be
// able to appear in both covariant and contravariant positions, it becomes
// incredibly unwieldy to manage variants of each constraint with `never`
// and `unknown` in all the right places
"@typescript-eslint/no-explicit-any": "off"
}
}
]
}
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ jobs:
- name: Build
run: yarn build
- name: Run Tests
run: yarn test
uses: GabrielBB/xvfb-action@v1
with:
run: yarn test

test-windows:
name: Test Windows
Expand Down Expand Up @@ -96,4 +98,6 @@ jobs:
- name: Build
run: yarn build
- name: Run Tests
run: yarn test
uses: GabrielBB/xvfb-action@v1
with:
run: yarn test
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceFolder}/packages/vscode"]
}
]
}
19 changes: 17 additions & 2 deletions packages/config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,28 @@ export { GlintEnvironment, GlintEnvironmentConfig } from './environment';

/**
* Loads glint configuration, starting from the given directory
* and searching upwards.
* and searching upwards and raising an error if no configuration
* is found.
*/
export function loadConfig(from: string): GlintConfig {
let config = findConfig(from);
if (!config) {
throw new Error(`Unable to find Glint configuration for ${from}`);
}

return config;
}

/**
* Loads glint configuration, starting from the given directory
* and searching upwards. Returns `null` if no configuration is
* found.
*/
export function findConfig(from: string): GlintConfig | null {
let result = cosmiconfigSync('glint').search(from);
if (result) {
return new GlintConfig(path.dirname(result.filepath), result.config);
}

throw new Error(`Unable to find Glint configuration for ${from}`);
return null;
}
170 changes: 87 additions & 83 deletions packages/core/src/language-server/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TextDocuments, TextDocumentSyncKind, createConnection } from 'vscode-languageserver/node';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { loadConfig } from '@glint/config';
import { findConfig } from '@glint/config';
import { loadTypeScript } from '../common/load-typescript';
import GlintLanguageServer from './glint-language-server';
import { parseConfigFile, uriToFilePath } from './util';
Expand All @@ -10,93 +10,97 @@ const connection = createConnection(process.stdin, process.stdout);
const documents = new TextDocuments(TextDocument);

const ts = loadTypeScript();
const glintConfig = loadConfig(process.cwd());
const glintConfig = findConfig(process.cwd());
const tsconfigPath = ts.findConfigFile(process.cwd(), ts.sys.fileExists);
const { fileNames, options } = parseConfigFile(ts, tsconfigPath);
const tsFileNames = fileNames.filter((fileName) => /\.ts$/.test(fileName));
const getRootFileNames = (): Array<string> => {
return tsFileNames.concat(documents.all().map((doc) => uriToFilePath(doc.uri)));
};

const gls = new GlintLanguageServer(ts, glintConfig, getRootFileNames, options);

connection.onInitialize(() => ({
capabilities: {
textDocumentSync: TextDocumentSyncKind.Full,
completionProvider: {
resolveProvider: true,
},
referencesProvider: true,
hoverProvider: true,
definitionProvider: true,
workspaceSymbolProvider: true,
renameProvider: {
prepareProvider: true,
if (glintConfig) {
const gls = new GlintLanguageServer(ts, glintConfig, getRootFileNames, options);

connection.onInitialize(() => ({
capabilities: {
textDocumentSync: TextDocumentSyncKind.Full,
completionProvider: {
resolveProvider: true,
},
referencesProvider: true,
hoverProvider: true,
definitionProvider: true,
workspaceSymbolProvider: true,
renameProvider: {
prepareProvider: true,
},
},
},
}));

const scheduleDiagnostics = debounce(250, () => {
for (let { uri } of documents.all()) {
connection.sendDiagnostics({
uri,
diagnostics: gls.getDiagnostics(uri),
});
}
});

connection.onDidChangeWatchedFiles(() => {
// TODO: use this to synchronize files that aren't open so we don't assume changes only
// happen in the editor.
});

documents.onDidOpen(({ document }) => {
gls.openFile(document.uri, document.getText());

scheduleDiagnostics();
});

documents.onDidClose(({ document }) => {
gls.closeFile(document.uri);
});

documents.onDidChangeContent(({ document }) => {
gls.updateFile(document.uri, document.getText());

scheduleDiagnostics();
});

connection.onPrepareRename(({ textDocument, position }) => {
return gls.prepareRename(textDocument.uri, position);
});

connection.onRenameRequest(({ textDocument, position, newName }) => {
return gls.getEditsForRename(textDocument.uri, position, newName);
});

connection.onCompletion(({ textDocument, position }) => {
return gls.getCompletions(textDocument.uri, position);
});

connection.onCompletionResolve((item) => {
return gls.getCompletionDetails(item);
});

connection.onHover(({ textDocument, position }) => {
return gls.getHover(textDocument.uri, position);
});

connection.onDefinition(({ textDocument, position }) => {
return gls.getDefinition(textDocument.uri, position);
});

connection.onReferences(({ textDocument, position }) => {
return gls.getReferences(textDocument.uri, position);
});

connection.onWorkspaceSymbol(({ query }) => {
return gls.findSymbols(query);
});

documents.listen(connection);
connection.listen();
}));

const scheduleDiagnostics = debounce(250, () => {
for (let { uri } of documents.all()) {
connection.sendDiagnostics({
uri,
diagnostics: gls.getDiagnostics(uri),
});
}
});

connection.onDidChangeWatchedFiles(() => {
// TODO: use this to synchronize files that aren't open so we don't assume changes only
// happen in the editor.
});

documents.onDidOpen(({ document }) => {
gls.openFile(document.uri, document.getText());

scheduleDiagnostics();
});

documents.onDidClose(({ document }) => {
gls.closeFile(document.uri);
});

documents.onDidChangeContent(({ document }) => {
gls.updateFile(document.uri, document.getText());

scheduleDiagnostics();
});

connection.onPrepareRename(({ textDocument, position }) => {
return gls.prepareRename(textDocument.uri, position);
});

connection.onRenameRequest(({ textDocument, position, newName }) => {
return gls.getEditsForRename(textDocument.uri, position, newName);
});

connection.onCompletion(({ textDocument, position }) => {
return gls.getCompletions(textDocument.uri, position);
});

connection.onCompletionResolve((item) => {
return gls.getCompletionDetails(item);
});

connection.onHover(({ textDocument, position }) => {
return gls.getHover(textDocument.uri, position);
});

connection.onDefinition(({ textDocument, position }) => {
return gls.getDefinition(textDocument.uri, position);
});

connection.onReferences(({ textDocument, position }) => {
return gls.getReferences(textDocument.uri, position);
});

connection.onWorkspaceSymbol(({ query }) => {
return gls.findSymbols(query);
});

documents.listen(connection);
connection.listen();
} else {
connection.console.info(`No Glint config found from ${process.cwd()}`);
}
5 changes: 5 additions & 0 deletions packages/vscode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.vscode-test/
*.vsix
lib/
tsconfig.tsbuildinfo
4 changes: 4 additions & 0 deletions packages/vscode/.vscodeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.vscode/**
.gitignore
**/*.map
**/.eslintrc.json
3 changes: 3 additions & 0 deletions packages/vscode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# A Visual Studio Code extension for the [glint] language server.

[glint]: https://github.com/typed-ember/glint
1 change: 1 addition & 0 deletions packages/vscode/__fixtures__/ember-app/.glintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
environment: ember-loose
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{this.message}}
5 changes: 5 additions & 0 deletions packages/vscode/__fixtures__/ember-app/app/components/foo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Component from '@glimmer/component';

export default class MyComponent extends Component {
private message = 'hello';
}
4 changes: 4 additions & 0 deletions packages/vscode/__fixtures__/ember-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "test-ember-app",
"private": true
}
9 changes: 9 additions & 0 deletions packages/vscode/__fixtures__/ember-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"strict": true,
"target": "es2019",
"module": "es2015",
"moduleResolution": "node",
"skipLibCheck": true
}
}
16 changes: 16 additions & 0 deletions packages/vscode/__tests__/helpers/async.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

export async function waitUntil(callback: () => unknown): Promise<void> {
let start = Date.now();
while (Date.now() - start < 5_000) {
if (await callback()) {
return;
}

await sleep(500);
}

throw new Error(`waitUntil condition never came true`);
}
Loading

0 comments on commit e06775b

Please sign in to comment.