Repository: elm-tooling/elm-language-server
Pull request #307 (2020-08-10 00:03:21)

Improve definition for conflicting module/type names and various completion improvements
Fixes definitions for conflicting names of types and modules. For example, a module import named `Model` and a type named `Model` conflicted before and broke things. To fix this, I detect if there is a dot after the name, then it is a module, or else its a type. 

There are also some various completions filtering and sorting improvements. Like if a module import is exposing values, we guess that the completions from that module should just be the value, without the module prefix. Also sort auto imports based on modules that are already imported. 

Razzeee: Seems good to be merged. But kinda curious why this isn't including some tests :)
jmbockhorst: Good point. I'll add a few tests. 

Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index 67a79ed..ccb81a7 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -21,7 +21,7 @@ module.exports = {
     project: ["./tsconfig.test.json"],
   },
   plugins: ["@typescript-eslint", "prettier"],
-  ignorePatterns: ["*.test.ts", "jest.config.js", ".eslintrc.js"],
+  ignorePatterns: ["*.test.ts"],
   rules: {
     "prettier/prettier": "error",
     "no-prototype-builtins": "warn",
diff --git a/jest.config.js b/jest.config.js
index dc81d91..4f2defa 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -4,5 +4,4 @@ module.exports = {
   testMatch: ["**/?(*.)+(spec|test).[tj]s?(x)"],
   collectCoverage: true,
   coverageReporters: ["lcov"],
-  setupFilesAfterEnv: ["<rootDir>/test/jest.setup.ts"],
 };
diff --git a/package-lock.json b/package-lock.json
index ba77889..6c68a08 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6014,11 +6014,6 @@
         "util-deprecate": "~1.0.1"
       }
     },
-    "reflect-metadata": {
-      "version": "0.1.13",
-      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
-      "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
-    },
     "regenerator-runtime": {
       "version": "0.9.6",
       "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz",
@@ -7338,7 +7333,8 @@
     "tslib": {
       "version": "1.13.0",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
-      "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
+      "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+      "dev": true
     },
     "tsutils": {
       "version": "3.17.1",
@@ -7349,14 +7345,6 @@
         "tslib": "^1.8.1"
       }
     },
-    "tsyringe": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.3.0.tgz",
-      "integrity": "sha512-Vzty1M/EQXSsEE8aoIOLl1l793chcPyQAnFJaS1mFDtFvNPY+jknSPwMIF6yfcjua+2GTgwxWFzuA3cjIx4NZA==",
-      "requires": {
-        "tslib": "^1.9.3"
-      }
-    },
     "tunnel-agent": {
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
diff --git a/package.json b/package.json
index 50032b4..5770ccc 100644
--- a/package.json
+++ b/package.json
@@ -21,8 +21,6 @@
     "fast-diff": "^1.2.0",
     "globby": "^11.0.1",
     "pjson": "1.0.9",
-    "reflect-metadata": "^0.1.13",
-    "tsyringe": "^4.3.0",
     "vscode-languageserver": "^6.1.1",
     "vscode-languageserver-textdocument": "1.0.1",
     "vscode-uri": "^2.1.2",
diff --git a/src/elmWorkspace.ts b/src/elmWorkspace.ts
index 88c6819..e94f833 100644
--- a/src/elmWorkspace.ts
+++ b/src/elmWorkspace.ts
@@ -1,8 +1,7 @@
 import fs from "fs";
-import globby from "globby";
 import os from "os";
+import globby from "globby";
 import path from "path";
-import { container } from "tsyringe";
 import util from "util";
 import { IConnection } from "vscode-languageserver";
 import { URI } from "vscode-uri";
@@ -39,19 +38,18 @@ export class ElmWorkspace implements IElmWorkspace {
   }[] = [];
   private forest: Forest = new Forest();
   private imports: Imports;
-  private parser: Parser;
-  private connection: IConnection;
-  private settings: Settings;
-
-  constructor(private rootPath: URI) {
-    this.settings = container.resolve("Settings");
-    this.connection = container.resolve("Connection");
-    this.parser = container.resolve("Parser");
+
+  constructor(
+    private rootPath: URI,
+    private connection: IConnection,
+    private settings: Settings,
+    private parser: Parser,
+  ) {
     this.connection.console.info(
       `Starting language server for folder: ${this.rootPath.toString()}`,
     );
 
-    this.imports = new Imports();
+    this.imports = new Imports(parser);
   }
 
   public async init(
diff --git a/src/imports.ts b/src/imports.ts
index c99b065..3cb76b0 100644
--- a/src/imports.ts
+++ b/src/imports.ts
@@ -1,7 +1,6 @@
 import Parser, { SyntaxNode, Tree } from "web-tree-sitter";
 import { IForest, ITreeContainer } from "./forest";
 import { IExposing, NodeType, TreeUtils } from "./util/treeUtils";
-import { container } from "tsyringe";
 
 export interface IImport {
   alias: string;
@@ -20,11 +19,8 @@ export interface IImports {
 
 export class Imports implements IImports {
   public imports?: { [uri: string]: IImport[] } = {};
-  private parser: Parser;
 
-  constructor() {
-    this.parser = container.resolve("Parser");
-  }
+  constructor(private parser: Parser) {}
 
   public updateImports(uri: string, tree: Tree, forest: IForest): void {
     const result: IImport[] = [];
diff --git a/src/index.ts b/src/index.ts
index dd96338..a7c3ac2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,8 +1,6 @@
 #!/usr/bin/env node
 
 import * as Path from "path";
-import "reflect-metadata";
-import { container } from "tsyringe"; //must be after reflect-metadata
 import {
   createConnection,
   IConnection,
@@ -11,11 +9,7 @@ import {
   ProposedFeatures,
 } from "vscode-languageserver";
 import Parser from "web-tree-sitter";
-import { CapabilityCalculator } from "./capabilityCalculator";
 import { ILanguageServer } from "./server";
-import { DocumentEvents } from "./util/documentEvents";
-import { Settings } from "./util/settings";
-import { TextDocumentEvents } from "./util/textDocumentEvents";
 
 // Show version for `-v` or `--version` arguments
 if (process.argv[2] === "-v" || process.argv[2] === "--version") {
@@ -29,19 +23,7 @@ if (process.argv.length === 2) {
   process.argv.push("--stdio");
 }
 
-// Composition root - be aware, there are some register calls that need to be done later
-container.register<IConnection>("Connection", {
-  useValue: createConnection(ProposedFeatures.all),
-});
-container.registerSingleton<Parser>("Parser", Parser);
-
-container.registerSingleton("DocumentEvents", DocumentEvents);
-container.register(TextDocumentEvents, {
-  useValue: new TextDocumentEvents(),
-});
-
-const connection = container.resolve<IConnection>("Connection");
-
+const connection: IConnection = createConnection(ProposedFeatures.all);
 let server: ILanguageServer;
 
 connection.onInitialize(
@@ -57,19 +39,11 @@ connection.onInitialize(
       `Loading Elm tree-sitter syntax from ${pathToWasm}`,
     );
     const language = await Parser.Language.load(pathToWasm);
-    container.resolve<Parser>("Parser").setLanguage(language);
-
-    container.register(CapabilityCalculator, {
-      useValue: new CapabilityCalculator(params.capabilities),
-    });
-    const initializationOptions = params.initializationOptions ?? {};
-
-    container.register("Settings", {
-      useValue: new Settings(initializationOptions, params.capabilities),
-    });
+    const parser = new Parser();
+    parser.setLanguage(language);
 
     const { Server } = await import("./server");
-    server = new Server(params, progress);
+    server = new Server(connection, params, parser, progress);
     await server.init();
 
     return server.capabilities;
diff --git a/src/providers/astProvider.ts b/src/providers/astProvider.ts
index ee15cb7..b0034f2 100644
--- a/src/providers/astProvider.ts
+++ b/src/providers/astProvider.ts
@@ -1,5 +1,5 @@
 import { readFileSync } from "fs";
-import { container } from "tsyringe";
+
 import {
   DidChangeTextDocumentParams,
   IConnection,
@@ -12,18 +12,18 @@ import { IDocumentEvents } from "../util/documentEvents";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 
 export class ASTProvider {
-  private connection: IConnection;
-  private parser: Parser;
-
-  constructor() {
-    this.parser = container.resolve("Parser");
-    this.connection = container.resolve<IConnection>("Connection");
-    const documentEvents = container.resolve<IDocumentEvents>("DocumentEvents");
-
+  constructor(
+    private connection: IConnection,
+    elmWorkspaces: IElmWorkspace[],
+    documentEvents: IDocumentEvents,
+    private parser: Parser,
+  ) {
     documentEvents.on(
       "change",
-      new ElmWorkspaceMatcher((params: DidChangeTextDocumentParams) =>
-        URI.parse(params.textDocument.uri),
+      new ElmWorkspaceMatcher(
+        elmWorkspaces,
+        (params: DidChangeTextDocumentParams) =>
+          URI.parse(params.textDocument.uri),
       ).handlerForWorkspace(this.handleChangeTextDocument),
     );
   }
diff --git a/src/providers/codeActionProvider.ts b/src/providers/codeActionProvider.ts
index 64ac22b..3698e4d 100644
--- a/src/providers/codeActionProvider.ts
+++ b/src/providers/codeActionProvider.ts
@@ -1,52 +1,46 @@
-import { container } from "tsyringe";
 import {
-  ApplyWorkspaceEditResponse,
   CodeAction,
   CodeActionKind,
   CodeActionParams,
   ExecuteCommandParams,
   IConnection,
+  ApplyWorkspaceEditParams,
+  ApplyWorkspaceEditResponse,
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import { SyntaxNode, Tree } from "web-tree-sitter";
 import { IElmWorkspace } from "../elmWorkspace";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
-import { RefactorEditUtils } from "../util/refactorEditUtils";
 import { Settings } from "../util/settings";
 import { TreeUtils } from "../util/treeUtils";
 import { ElmAnalyseDiagnostics } from "./diagnostics/elmAnalyseDiagnostics";
 import { ElmMakeDiagnostics } from "./diagnostics/elmMakeDiagnostics";
-import { ExposeUnexposeHandler } from "./handlers/exposeUnexposeHandler";
 import { MoveRefactoringHandler } from "./handlers/moveRefactoringHandler";
+import { ExposeUnexposeHandler } from "./handlers/exposeUnexposeHandler";
+import { RefactorEditUtils } from "../util/refactorEditUtils";
 
 export class CodeActionProvider {
-  private connection: IConnection;
-  private settings: Settings;
-  private elmAnalyse: ElmAnalyseDiagnostics | null;
-  private elmMake: ElmMakeDiagnostics;
-
-  constructor() {
-    this.elmAnalyse = container.resolve<ElmAnalyseDiagnostics | null>(
-      ElmAnalyseDiagnostics,
-    );
-    this.elmMake = container.resolve<ElmMakeDiagnostics>(ElmMakeDiagnostics);
-    this.settings = container.resolve("Settings");
-    this.connection = container.resolve<IConnection>("Connection");
-
+  constructor(
+    private connection: IConnection,
+    private elmWorkspaces: IElmWorkspace[],
+    private settings: Settings,
+    private elmAnalyse: ElmAnalyseDiagnostics | null,
+    private elmMake: ElmMakeDiagnostics,
+  ) {
     this.onCodeAction = this.onCodeAction.bind(this);
     this.onExecuteCommand = this.onExecuteCommand.bind(this);
     this.connection.onCodeAction(
-      new ElmWorkspaceMatcher((param: CodeActionParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: CodeActionParams) =>
         URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.onCodeAction),
     );
     this.connection.onExecuteCommand(this.onExecuteCommand);
 
-    if (this.settings.extendedCapabilities?.moveFunctionRefactoringSupport) {
-      new MoveRefactoringHandler();
+    if (settings.extendedCapabilities?.moveFunctionRefactoringSupport) {
+      new MoveRefactoringHandler(this.connection, this.elmWorkspaces);
     }
 
-    new ExposeUnexposeHandler();
+    new ExposeUnexposeHandler(this.connection, this.elmWorkspaces);
   }
 
   private onCodeAction(
diff --git a/src/providers/codeLensProvider.ts b/src/providers/codeLensProvider.ts
index c576e95..f68849d 100644
--- a/src/providers/codeLensProvider.ts
+++ b/src/providers/codeLensProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   CodeLens,
   CodeLensParams,
@@ -13,26 +12,25 @@ import { SyntaxNode, Tree } from "web-tree-sitter";
 import { IElmWorkspace } from "../elmWorkspace";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { References } from "../util/references";
-import { Settings } from "../util/settings";
 import { TreeUtils } from "../util/treeUtils";
+import { Settings } from "../util/settings";
 
 type CodeLensType = "exposed" | "referenceCounter";
 type CodeLensResult = CodeLens[] | null | undefined;
 
 export class CodeLensProvider {
-  private readonly connection: IConnection;
-  private readonly settings: Settings;
-
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
-    this.settings = container.resolve<Settings>("Settings");
+  constructor(
+    private readonly connection: IConnection,
+    elmWorkspaces: IElmWorkspace[],
+    private settings: Settings,
+  ) {
     this.connection.onCodeLens(
-      new ElmWorkspaceMatcher((param: CodeLensParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: CodeLensParams) =>
         URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.handleCodeLensRequest),
     );
     this.connection.onCodeLensResolve(
-      new ElmWorkspaceMatcher((param: CodeLens) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: CodeLens) =>
         URI.parse(param.data.uri),
       ).handlerForWorkspace(this.handleCodeLensResolveRequest),
     );
diff --git a/src/providers/completionProvider.ts b/src/providers/completionProvider.ts
index 69f57f9..2d1925b 100644
--- a/src/providers/completionProvider.ts
+++ b/src/providers/completionProvider.ts
@@ -1,8 +1,6 @@
-import { container } from "tsyringe";
 import {
   CompletionItem,
   CompletionItemKind,
-  CompletionList,
   CompletionParams,
   IConnection,
   InsertTextFormat,
@@ -10,20 +8,22 @@ import {
   Position,
   Range,
   TextEdit,
+  CompletionList,
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import { SyntaxNode, Tree } from "web-tree-sitter";
 import { IElmWorkspace } from "../elmWorkspace";
 import { IForest, ITreeContainer } from "../forest";
 import { IImports } from "../imports";
-import { comparePosition, PositionUtil } from "../positionUtil";
 import { getEmptyTypes } from "../util/elmUtils";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { HintHelper } from "../util/hintHelper";
-import { ImportUtils } from "../util/importUtils";
-import { RefactorEditUtils } from "../util/refactorEditUtils";
 import { TreeUtils } from "../util/treeUtils";
 import RANKING_LIST from "./ranking";
+import { ImportUtils } from "../util/importUtils";
+import { RefactorEditUtils } from "../util/refactorEditUtils";
+import { Utils } from "../util/utils";
+import { comparePosition, PositionUtil } from "../positionUtil";
 
 export type CompletionResult =
   | CompletionItem[]
@@ -44,12 +44,10 @@ interface ICompletionOptions {
 
 export class CompletionProvider {
   private qidRegex = /[a-zA-Z0-9.]+/;
-  private connection: IConnection;
 
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
-    this.connection.onCompletion(
-      new ElmWorkspaceMatcher((param: CompletionParams) =>
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
+    connection.onCompletion(
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: CompletionParams) =>
         URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.handleCompletionRequest),
     );
@@ -346,6 +344,7 @@ export class CompletionProvider {
 
       completions.push(
         ...this.getCompletionsFromOtherFile(
+          tree,
           elmWorkspace.getImports(),
           params.textDocument.uri,
           replaceRange,
@@ -495,6 +494,7 @@ export class CompletionProvider {
   }
 
   private getCompletionsFromOtherFile(
+    tree: Tree,
     imports: IImports,
     uri: string,
     range: Range,
@@ -523,8 +523,22 @@ export class CompletionProvider {
         const dotIndex = label.lastIndexOf(".");
         const valuePart = label.slice(dotIndex + 1);
 
+        const importNode = TreeUtils.findImportClauseByName(
+          tree,
+          element.fromModuleName,
+        );
+
+        // Check if a value is already imported for this module using the exposing list
+        // In this case, we want to prefex the unqualified value since they are using the import exposing list
+        const valuesAlreadyExposed =
+          importNode &&
+          !!TreeUtils.findFirstNamedChildOfType("exposing_list", importNode);
+
         // Try to determine if just the value is being typed
-        if (valuePart.toLowerCase().startsWith(inputText.toLowerCase())) {
+        if (
+          !valuesAlreadyExposed &&
+          valuePart.toLowerCase().startsWith(inputText.toLowerCase())
+        ) {
           filterText = valuePart;
         }
 
@@ -1003,11 +1017,13 @@ export class CompletionProvider {
         possibleImport.valueToImport ?? possibleImport.value,
       );
 
+      const sortText = i < 10 ? `0${i}` : i;
+
       const completionOptions = {
         markdownDocumentation,
         label: possibleImport.value,
         range,
-        sortPrefix: `f${i}`,
+        sortPrefix: `f${sortText}`,
         detail,
         additionalTextEdits: importTextEdit ? [importTextEdit] : undefined,
       };
diff --git a/src/providers/definitionProvider.ts b/src/providers/definitionProvider.ts
index e735f0e..ee31a25 100644
--- a/src/providers/definitionProvider.ts
+++ b/src/providers/definitionProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   IConnection,
   Location,
@@ -21,12 +20,12 @@ export type DefinitionResult =
   | undefined;
 
 export class DefinitionProvider {
-  private connection: IConnection;
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
     this.connection.onDefinition(
-      new ElmWorkspaceMatcher((param: TextDocumentPositionParams) =>
-        URI.parse(param.textDocument.uri),
+      new ElmWorkspaceMatcher(
+        elmWorkspaces,
+        (param: TextDocumentPositionParams) =>
+          URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.handleDefinitionRequest),
     );
   }
diff --git a/src/providers/diagnostics/diagnosticsProvider.ts b/src/providers/diagnostics/diagnosticsProvider.ts
index f90fe86..60e8b45 100644
--- a/src/providers/diagnostics/diagnosticsProvider.ts
+++ b/src/providers/diagnostics/diagnosticsProvider.ts
@@ -1,7 +1,7 @@
-import { container, injectable } from "tsyringe";
 import { Diagnostic, FileChangeType, IConnection } from "vscode-languageserver";
 import { TextDocument } from "vscode-languageserver-textdocument";
 import { URI } from "vscode-uri";
+import { IElmWorkspace } from "../../elmWorkspace";
 import { ElmWorkspaceMatcher } from "../../util/elmWorkspaceMatcher";
 import { NoWorkspaceContainsError } from "../../util/noWorkspaceContainsError";
 import { ElmAnalyseTrigger, Settings } from "../../util/settings";
@@ -24,7 +24,6 @@ export interface IElmIssue {
   file: string;
 }
 
-@injectable()
 export class DiagnosticsProvider {
   private elmMakeDiagnostics: ElmMakeDiagnostics;
   private elmAnalyseDiagnostics: ElmAnalyseDiagnostics | null;
@@ -34,22 +33,19 @@ export class DiagnosticsProvider {
     elmAnalyse: Map<string, Diagnostic[]>;
     elmTest: Map<string, Diagnostic[]>;
   };
-  private events: TextDocumentEvents;
-  private connection: IConnection;
-  private settings: Settings;
 
-  constructor() {
-    this.elmAnalyseDiagnostics = container.resolve<ElmAnalyseDiagnostics | null>(
-      ElmAnalyseDiagnostics,
-    );
-    this.elmMakeDiagnostics = container.resolve<ElmMakeDiagnostics>(
-      ElmMakeDiagnostics,
-    );
-    this.settings = container.resolve("Settings");
-    this.connection = container.resolve<IConnection>("Connection");
-    this.events = container.resolve<TextDocumentEvents>(TextDocumentEvents);
+  constructor(
+    private connection: IConnection,
+    elmWorkspaces: IElmWorkspace[],
+    private settings: Settings,
+    private events: TextDocumentEvents<TextDocument>,
+    elmAnalyse: ElmAnalyseDiagnostics | null,
+    elmMake: ElmMakeDiagnostics,
+  ) {
     this.newElmAnalyseDiagnostics = this.newElmAnalyseDiagnostics.bind(this);
-    this.elmWorkspaceMatcher = new ElmWorkspaceMatcher((doc) =>
+    this.elmMakeDiagnostics = elmMake;
+    this.elmAnalyseDiagnostics = elmAnalyse;
+    this.elmWorkspaceMatcher = new ElmWorkspaceMatcher(elmWorkspaces, (doc) =>
       URI.parse(doc.uri),
     );
 
diff --git a/src/providers/diagnostics/elmAnalyseDiagnostics.ts b/src/providers/diagnostics/elmAnalyseDiagnostics.ts
index 1b89b58..2d8fff0 100644
--- a/src/providers/diagnostics/elmAnalyseDiagnostics.ts
+++ b/src/providers/diagnostics/elmAnalyseDiagnostics.ts
@@ -3,10 +3,8 @@ import { ElmApp, FixedFile, Message, Report } from "elm-analyse/ts/domain";
 import { EventEmitter } from "events";
 import * as fs from "fs";
 import * as path from "path";
-import { container, injectable } from "tsyringe";
 import util from "util";
 import {
-  ApplyWorkspaceEditResponse,
   CodeAction,
   CodeActionKind,
   CodeActionParams,
@@ -16,6 +14,7 @@ import {
   ExecuteCommandParams,
   IConnection,
   TextEdit,
+  ApplyWorkspaceEditResponse,
 } from "vscode-languageserver";
 import { TextDocument } from "vscode-languageserver-textdocument";
 import { URI } from "vscode-uri";
@@ -47,28 +46,27 @@ export interface IElmAnalyseEvents {
   on(event: "new-report", diagnostics: Map<string, Diagnostic[]>): this;
 }
 
-@injectable()
 export class ElmAnalyseDiagnostics {
   private elmAnalysers: Map<IElmWorkspace, Promise<ElmApp>>;
   private diagnostics: Map<string, Diagnostic[]>;
   private filesWithDiagnostics: Set<string> = new Set();
   private eventEmitter: EventEmitter = new EventEmitter();
   private elmWorkspaceMatcher: ElmWorkspaceMatcher<URI>;
-  private events: TextDocumentEvents;
-  private connection: IConnection;
-  private settings: Settings;
-  private formattingProvider: DocumentFormattingProvider;
-
-  constructor() {
-    const elmWorkspaces = container.resolve<IElmWorkspace[]>("ElmWorkspaces");
-    this.formattingProvider = container.resolve(DocumentFormattingProvider);
-    this.settings = container.resolve("Settings");
-    this.connection = container.resolve<IConnection>("Connection");
-    this.events = container.resolve<TextDocumentEvents>(TextDocumentEvents);
+
+  constructor(
+    private connection: IConnection,
+    elmWorkspaces: IElmWorkspace[],
+    private events: TextDocumentEvents<TextDocument>,
+    private settings: Settings,
+    private formattingProvider: DocumentFormattingProvider,
+  ) {
     this.onExecuteCommand = this.onExecuteCommand.bind(this);
     this.onCodeAction = this.onCodeAction.bind(this);
     this.diagnostics = new Map<string, Diagnostic[]>();
-    this.elmWorkspaceMatcher = new ElmWorkspaceMatcher((uri) => uri);
+    this.elmWorkspaceMatcher = new ElmWorkspaceMatcher(
+      elmWorkspaces,
+      (uri) => uri,
+    );
 
     this.elmAnalysers = new Map(
       elmWorkspaces.map((ws) => [ws, this.setupElmAnalyse(ws)]),
diff --git a/src/providers/diagnostics/elmMakeDiagnostics.ts b/src/providers/diagnostics/elmMakeDiagnostics.ts
index c45bbf1..110fe37 100644
--- a/src/providers/diagnostics/elmMakeDiagnostics.ts
+++ b/src/providers/diagnostics/elmMakeDiagnostics.ts
@@ -1,7 +1,6 @@
 /* eslint-disable @typescript-eslint/no-unsafe-call */
 import { randomBytes } from "crypto";
 import * as path from "path";
-import { container } from "tsyringe";
 import {
   CodeAction,
   CodeActionKind,
@@ -11,19 +10,21 @@ import {
   TextEdit,
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
-import { Forest, ITreeContainer } from "../../forest";
-import { IImports } from "../../imports";
+import { IElmWorkspace } from "../../elmWorkspace";
 import * as utils from "../../util/elmUtils";
 import { execCmd } from "../../util/elmUtils";
 import { ElmWorkspaceMatcher } from "../../util/elmWorkspaceMatcher";
-import { ImportUtils } from "../../util/importUtils";
-import { RefactorEditUtils } from "../../util/refactorEditUtils";
 import { Settings } from "../../util/settings";
-import { TreeUtils } from "../../util/treeUtils";
-import { Utils } from "../../util/utils";
 import { IElmIssue } from "./diagnosticsProvider";
 import { ElmDiagnosticsHelper } from "./elmDiagnosticsHelper";
 import execa = require("execa");
+import { RefactorEditUtils } from "../../util/refactorEditUtils";
+import { ImportUtils } from "../../util/importUtils";
+import { TreeUtils } from "../../util/treeUtils";
+import { Utils } from "../../util/utils";
+import { ITreeContainer } from "../../forest";
+import { Forest } from "../../forest";
+import { IImports } from "../../imports";
 
 const ELM_MAKE = "Elm";
 const NAMING_ERROR = "NAMING ERROR";
@@ -73,13 +74,16 @@ export class ElmMakeDiagnostics {
     string,
     { moduleName: string; valueName?: string; diagnostic: Diagnostic }[]
   >();
-  private settings: Settings;
-  private connection: IConnection;
 
-  constructor() {
-    this.settings = container.resolve("Settings");
-    this.connection = container.resolve<IConnection>("Connection");
-    this.elmWorkspaceMatcher = new ElmWorkspaceMatcher((uri) => uri);
+  constructor(
+    private connection: IConnection,
+    elmWorkspaces: IElmWorkspace[],
+    private settings: Settings,
+  ) {
+    this.elmWorkspaceMatcher = new ElmWorkspaceMatcher(
+      elmWorkspaces,
+      (uri) => uri,
+    );
   }
 
   public createDiagnostics = async (
diff --git a/src/providers/documentFormatingProvider.ts b/src/providers/documentFormatingProvider.ts
index 61c9967..f4505d3 100644
--- a/src/providers/documentFormatingProvider.ts
+++ b/src/providers/documentFormatingProvider.ts
@@ -1,9 +1,9 @@
-import { container, injectable } from "tsyringe";
 import {
   DocumentFormattingParams,
   IConnection,
   TextEdit,
 } from "vscode-languageserver";
+import { TextDocument } from "vscode-languageserver-textdocument";
 import { URI } from "vscode-uri";
 import { IElmWorkspace } from "../elmWorkspace";
 import * as Diff from "../util/diff";
@@ -14,19 +14,18 @@ import { TextDocumentEvents } from "../util/textDocumentEvents";
 
 type DocumentFormattingResult = Promise<TextEdit[] | undefined>;
 
-@injectable()
 export class DocumentFormattingProvider {
-  private events: TextDocumentEvents;
-  private connection: IConnection;
-  private settings: Settings;
-
-  constructor() {
-    this.settings = container.resolve<Settings>("Settings");
-    this.connection = container.resolve<IConnection>("Connection");
-    this.events = container.resolve<TextDocumentEvents>(TextDocumentEvents);
+  constructor(
+    private connection: IConnection,
+    elmWorkspaces: IElmWorkspace[],
+    private events: TextDocumentEvents<TextDocument>,
+    private settings: Settings,
+  ) {
     this.connection.onDocumentFormatting(
-      new ElmWorkspaceMatcher((params: DocumentFormattingParams) =>
-        URI.parse(params.textDocument.uri),
+      new ElmWorkspaceMatcher(
+        elmWorkspaces,
+        (params: DocumentFormattingParams) =>
+          URI.parse(params.textDocument.uri),
       ).handlerForWorkspace(this.handleFormattingRequest),
     );
   }
diff --git a/src/providers/documentSymbolProvider.ts b/src/providers/documentSymbolProvider.ts
index a9e615b..ed7f06b 100644
--- a/src/providers/documentSymbolProvider.ts
+++ b/src/providers/documentSymbolProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   DocumentSymbol,
   DocumentSymbolParams,
@@ -18,12 +17,9 @@ type DocumentSymbolResult =
   | undefined;
 
 export class DocumentSymbolProvider {
-  private connection: IConnection;
-
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
-    this.connection.onDocumentSymbol(
-      new ElmWorkspaceMatcher((param: DocumentSymbolParams) =>
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
+    connection.onDocumentSymbol(
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: DocumentSymbolParams) =>
         URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.handleDocumentSymbolRequest),
     );
diff --git a/src/providers/foldingProvider.ts b/src/providers/foldingProvider.ts
index cb7335e..37e1fa8 100644
--- a/src/providers/foldingProvider.ts
+++ b/src/providers/foldingProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   FoldingRange,
   FoldingRangeKind,
@@ -24,12 +23,12 @@ export class FoldingRangeProvider {
     "then",
     "else",
   ]);
-  private connection: IConnection;
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
-    this.connection.onFoldingRanges(
-      new ElmWorkspaceMatcher((param: FoldingRangeRequestParam) =>
-        URI.parse(param.textDocument.uri),
+
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
+    connection.onFoldingRanges(
+      new ElmWorkspaceMatcher(
+        elmWorkspaces,
+        (param: FoldingRangeRequestParam) => URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.handleFoldingRange),
     );
   }
diff --git a/src/providers/handlers/exposeUnexposeHandler.ts b/src/providers/handlers/exposeUnexposeHandler.ts
index 17aed13..3c8b426 100644
--- a/src/providers/handlers/exposeUnexposeHandler.ts
+++ b/src/providers/handlers/exposeUnexposeHandler.ts
@@ -1,30 +1,23 @@
-import { container } from "tsyringe";
 import { IConnection } from "vscode-languageserver";
-import { URI } from "vscode-uri";
 import { IElmWorkspace } from "../../elmWorkspace";
-import {
-  ExposeRequest,
-  IExposeUnexposeParams,
-  UnexposeRequest,
-} from "../../protocol";
 import { ElmWorkspaceMatcher } from "../../util/elmWorkspaceMatcher";
+import { URI } from "vscode-uri";
+import { ExposeRequest, UnexposeRequest } from "../../protocol";
+import { IExposeUnexposeParams } from "../../protocol";
 import { RefactorEditUtils } from "../../util/refactorEditUtils";
 
 export class ExposeUnexposeHandler {
-  private connection: IConnection;
-
-  constructor() {
-    this.connection = container.resolve("Connection");
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
     this.connection.onRequest(
       ExposeRequest,
-      new ElmWorkspaceMatcher((params: IExposeUnexposeParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (params: IExposeUnexposeParams) =>
         URI.parse(params.uri),
       ).handlerForWorkspace(this.handleExposeRequest.bind(this)),
     );
 
     this.connection.onRequest(
       UnexposeRequest,
-      new ElmWorkspaceMatcher((params: IExposeUnexposeParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (params: IExposeUnexposeParams) =>
         URI.parse(params.uri),
       ).handlerForWorkspace(this.handleUnexposeRequest.bind(this)),
     );
diff --git a/src/providers/handlers/moveRefactoringHandler.ts b/src/providers/handlers/moveRefactoringHandler.ts
index af06281..f8369e7 100644
--- a/src/providers/handlers/moveRefactoringHandler.ts
+++ b/src/providers/handlers/moveRefactoringHandler.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import { IConnection, Position, Range, TextEdit } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import { IElmWorkspace } from "../../elmWorkspace";
@@ -15,20 +14,20 @@ import { References } from "../../util/references";
 import { TreeUtils } from "../../util/treeUtils";
 
 export class MoveRefactoringHandler {
-  private connection: IConnection;
-
-  constructor() {
-    this.connection = container.resolve("Connection");
+  constructor(
+    private connection: IConnection,
+    private elmWorkspaces: IElmWorkspace[],
+  ) {
     this.connection.onRequest(
       GetMoveDestinationRequest,
-      new ElmWorkspaceMatcher((param: IMoveParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: IMoveParams) =>
         URI.parse(param.sourceUri),
       ).handlerForWorkspace(this.handleGetMoveDestinationsRequest.bind(this)),
     );
 
     this.connection.onRequest(
       MoveRequest,
-      new ElmWorkspaceMatcher((param: IMoveParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: IMoveParams) =>
         URI.parse(param.sourceUri),
       ).handlerForWorkspace(this.handleMoveRequest.bind(this)),
     );
diff --git a/src/providers/hoverProvider.ts b/src/providers/hoverProvider.ts
index a6ef97a..c0a65dd 100644
--- a/src/providers/hoverProvider.ts
+++ b/src/providers/hoverProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   Hover,
   IConnection,
@@ -16,13 +15,12 @@ import { NodeType, TreeUtils } from "../util/treeUtils";
 type HoverResult = Hover | null | undefined;
 
 export class HoverProvider {
-  private connection: IConnection;
-
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
     this.connection.onHover(
-      new ElmWorkspaceMatcher((param: TextDocumentPositionParams) =>
-        URI.parse(param.textDocument.uri),
+      new ElmWorkspaceMatcher(
+        elmWorkspaces,
+        (param: TextDocumentPositionParams) =>
+          URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.handleHoverRequest),
     );
   }
diff --git a/src/providers/referencesProvider.ts b/src/providers/referencesProvider.ts
index 805464e..d02263a 100644
--- a/src/providers/referencesProvider.ts
+++ b/src/providers/referencesProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   IConnection,
   Location,
@@ -16,11 +15,9 @@ import { TreeUtils } from "../util/treeUtils";
 type ReferenceResult = Location[] | null | undefined;
 
 export class ReferencesProvider {
-  private connection: IConnection;
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
     this.connection.onReferences(
-      new ElmWorkspaceMatcher((param: ReferenceParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: ReferenceParams) =>
         URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.handleReferencesRequest),
     );
diff --git a/src/providers/renameProvider.ts b/src/providers/renameProvider.ts
index 37f4cc9..43ca8bf 100644
--- a/src/providers/renameProvider.ts
+++ b/src/providers/renameProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   IConnection,
   Position,
@@ -19,18 +18,15 @@ import { References } from "../util/references";
 import { TreeUtils } from "../util/treeUtils";
 
 export class RenameProvider {
-  private connection: IConnection;
-
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
     this.connection.onPrepareRename(
-      new ElmWorkspaceMatcher((params: PrepareRenameParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (params: PrepareRenameParams) =>
         URI.parse(params.textDocument.uri),
       ).handlerForWorkspace(this.handlePrepareRenameRequest),
     );
 
     this.connection.onRenameRequest(
-      new ElmWorkspaceMatcher((params: RenameParams) =>
+      new ElmWorkspaceMatcher(elmWorkspaces, (params: RenameParams) =>
         URI.parse(params.textDocument.uri),
       ).handlerForWorkspace(this.handleRenameRequest),
     );
diff --git a/src/providers/selectionRangeProvider.ts b/src/providers/selectionRangeProvider.ts
index 76efab2..57e5db7 100644
--- a/src/providers/selectionRangeProvider.ts
+++ b/src/providers/selectionRangeProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   IConnection,
   Position,
@@ -14,12 +13,9 @@ import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { TreeUtils } from "../util/treeUtils";
 
 export class SelectionRangeProvider {
-  private connection: IConnection;
-
-  constructor() {
-    this.connection = container.resolve<IConnection>("Connection");
-    this.connection.onSelectionRanges(
-      new ElmWorkspaceMatcher((param: SelectionRangeParams) =>
+  constructor(private connection: IConnection, elmWorkspaces: IElmWorkspace[]) {
+    connection.onSelectionRanges(
+      new ElmWorkspaceMatcher(elmWorkspaces, (param: SelectionRangeParams) =>
         URI.parse(param.textDocument.uri),
       ).handlerForWorkspace(this.handleSelectionRangeRequest),
     );
diff --git a/src/providers/workspaceSymbolProvider.ts b/src/providers/workspaceSymbolProvider.ts
index 767fed0..b12123b 100644
--- a/src/providers/workspaceSymbolProvider.ts
+++ b/src/providers/workspaceSymbolProvider.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import {
   IConnection,
   SymbolInformation,
@@ -9,12 +8,10 @@ import { IElmWorkspace } from "../elmWorkspace";
 import { SymbolInformationTranslator } from "../util/symbolTranslator";
 
 export class WorkspaceSymbolProvider {
-  private readonly connection: IConnection;
-  private readonly elmWorkspaces: IElmWorkspace[];
-
-  constructor() {
-    this.elmWorkspaces = container.resolve<IElmWorkspace[]>("ElmWorkspaces");
-    this.connection = container.resolve<IConnection>("Connection");
+  constructor(
+    private readonly connection: IConnection,
+    private readonly elmWorkspaces: IElmWorkspace[],
+  ) {
     this.connection.onWorkspaceSymbol(this.workspaceSymbolRequest);
   }
 
diff --git a/src/server.ts b/src/server.ts
index 79c182d..30c5b0f 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -1,15 +1,16 @@
 import globby from "globby";
 import path from "path";
-import { container } from "tsyringe";
 import {
-  IConnection,
+  Connection,
   InitializeParams,
   InitializeResult,
 } from "vscode-languageserver";
+import { TextDocument } from "vscode-languageserver-textdocument";
 import { WorkDoneProgress } from "vscode-languageserver/lib/progress";
 import { URI } from "vscode-uri";
+import Parser from "web-tree-sitter";
 import { CapabilityCalculator } from "./capabilityCalculator";
-import { ElmWorkspace, IElmWorkspace } from "./elmWorkspace";
+import { ElmWorkspace } from "./elmWorkspace";
 import {
   ASTProvider,
   CodeActionProvider,
@@ -28,7 +29,9 @@ import {
   SelectionRangeProvider,
   WorkspaceSymbolProvider,
 } from "./providers";
+import { DocumentEvents } from "./util/documentEvents";
 import { Settings } from "./util/settings";
+import { TextDocumentEvents } from "./util/textDocumentEvents";
 
 export interface ILanguageServer {
   readonly capabilities: InitializeResult;
@@ -37,10 +40,23 @@ export interface ILanguageServer {
 }
 
 export class Server implements ILanguageServer {
-  private connection: IConnection;
-
-  constructor(params: InitializeParams, private progress: WorkDoneProgress) {
-    this.connection = container.resolve("Connection");
+  private calculator: CapabilityCalculator;
+  private settings: Settings;
+  private elmWorkspaces: ElmWorkspace[] = [];
+
+  constructor(
+    private connection: Connection,
+    private params: InitializeParams,
+    private parser: Parser,
+    private progress: WorkDoneProgress,
+  ) {
+    this.calculator = new CapabilityCalculator(params.capabilities);
+    const initializationOptions = this.params.initializationOptions ?? {};
+    this.settings = new Settings(
+      this.connection,
+      initializationOptions,
+      params.capabilities,
+    );
 
     const uri = this.getWorkspaceUri(params);
 
@@ -54,7 +70,7 @@ export class Server implements ILanguageServer {
         { suppressErrors: true },
       );
       if (elmJsons.length > 0) {
-        this.connection.console.info(
+        connection.console.info(
           `Found ${elmJsons.length} elm.json files for workspace ${globUri}`,
         );
         const listOfElmJsonFolders = elmJsons.map((a) =>
@@ -63,16 +79,19 @@ export class Server implements ILanguageServer {
         const topLevelElmJsons: Map<string, URI> = this.findTopLevelFolders(
           listOfElmJsonFolders,
         );
-        this.connection.console.info(
+        connection.console.info(
           `Found ${topLevelElmJsons.size} unique elmWorkspaces for workspace ${globUri}`,
         );
 
-        const elmWorkspaces: ElmWorkspace[] = [];
         topLevelElmJsons.forEach((elmWorkspace) => {
-          elmWorkspaces.push(new ElmWorkspace(elmWorkspace));
-        });
-        container.register("ElmWorkspaces", {
-          useValue: elmWorkspaces,
+          this.elmWorkspaces.push(
+            new ElmWorkspace(
+              elmWorkspace,
+              connection,
+              this.settings,
+              this.parser,
+            ),
+          );
         });
       } else {
         this.connection.window.showErrorMessage(
@@ -86,19 +105,15 @@ export class Server implements ILanguageServer {
   }
 
   get capabilities(): InitializeResult {
-    const calculator: CapabilityCalculator = container.resolve(
-      CapabilityCalculator,
-    );
     return {
-      capabilities: calculator.capabilities,
+      capabilities: this.calculator.capabilities,
     };
   }
 
   public async init(): Promise<void> {
     this.progress.begin("Indexing Elm", 0);
-    const elmWorkspaces = container.resolve<IElmWorkspace[]>("ElmWorkspaces");
     await Promise.all(
-      elmWorkspaces
+      this.elmWorkspaces
         .map((ws) => ({ ws, indexedPercent: 0 }))
         .map((indexingWs, _, all) =>
           indexingWs.ws.init((percent: number) => {
@@ -117,44 +132,74 @@ export class Server implements ILanguageServer {
   }
 
   public async registerInitializedProviders(): Promise<void> {
-    const settings = container.resolve<Settings>("Settings");
     // We can now query the client for up to date settings
-    settings.initFinished();
+    this.settings.initFinished();
 
-    // these register calls rely on settings having been setup
-    container.register(DocumentFormattingProvider, {
-      useValue: new DocumentFormattingProvider(),
-    });
-
-    container.register(ElmMakeDiagnostics, {
-      useValue: new ElmMakeDiagnostics(),
-    });
+    const documentEvents = new DocumentEvents(this.connection);
+    const textDocumentEvents = new TextDocumentEvents(
+      TextDocument,
+      documentEvents,
+    );
 
-    const clientSettings = await settings.getClientSettings();
+    const settings = await this.settings.getClientSettings();
 
-    container.register(ElmAnalyseDiagnostics, {
-      useValue:
-        clientSettings.elmAnalyseTrigger !== "never"
-          ? new ElmAnalyseDiagnostics()
-          : null,
-    });
+    const documentFormattingProvider = new DocumentFormattingProvider(
+      this.connection,
+      this.elmWorkspaces,
+      textDocumentEvents,
+      this.settings,
+    );
 
-    new DiagnosticsProvider();
+    const elmAnalyse =
+      settings.elmAnalyseTrigger !== "never"
+        ? new ElmAnalyseDiagnostics(
+            this.connection,
+            this.elmWorkspaces,
+            textDocumentEvents,
+            this.settings,
+            documentFormattingProvider,
+          )
+        : null;
+
+    const elmMake = new ElmMakeDiagnostics(
+      this.connection,
+      this.elmWorkspaces,
+      this.settings,
+    );
 
-    new CodeActionProvider();
+    new DiagnosticsProvider(
+      this.connection,
+      this.elmWorkspaces,
+      this.settings,
+      textDocumentEvents,
+      elmAnalyse,
+      elmMake,
+    );
 
-    new ASTProvider();
+    new CodeActionProvider(
+      this.connection,
+      this.elmWorkspaces,
+      this.settings,
+      elmAnalyse,
+      elmMake,
+    );
 
-    new FoldingRangeProvider();
-    new CompletionProvider();
-    new HoverProvider();
-    new DefinitionProvider();
-    new ReferencesProvider();
-    new DocumentSymbolProvider();
-    new WorkspaceSymbolProvider();
-    new CodeLensProvider();
-    new SelectionRangeProvider();
-    new RenameProvider();
+    new ASTProvider(
+      this.connection,
+      this.elmWorkspaces,
+      documentEvents,
+      this.parser,
+    );
+    new FoldingRangeProvider(this.connection, this.elmWorkspaces);
+    new CompletionProvider(this.connection, this.elmWorkspaces);
+    new HoverProvider(this.connection, this.elmWorkspaces);
+    new DefinitionProvider(this.connection, this.elmWorkspaces);
+    new ReferencesProvider(this.connection, this.elmWorkspaces);
+    new DocumentSymbolProvider(this.connection, this.elmWorkspaces);
+    new WorkspaceSymbolProvider(this.connection, this.elmWorkspaces);
+    new CodeLensProvider(this.connection, this.elmWorkspaces, this.settings);
+    new SelectionRangeProvider(this.connection, this.elmWorkspaces);
+    new RenameProvider(this.connection, this.elmWorkspaces);
   }
 
   private getElmJsonFolder(uri: string): URI {
diff --git a/src/util/documentEvents.ts b/src/util/documentEvents.ts
index 9a5a0e4..9a5f0fd 100644
--- a/src/util/documentEvents.ts
+++ b/src/util/documentEvents.ts
@@ -1,12 +1,11 @@
 import { EventEmitter } from "events";
 import {
+  Connection,
   DidChangeTextDocumentParams,
   DidCloseTextDocumentParams,
   DidOpenTextDocumentParams,
   DidSaveTextDocumentParams,
-  IConnection,
 } from "vscode-languageserver";
-import { injectable, container } from "tsyringe";
 
 type DidChangeCallback = (params: DidChangeTextDocumentParams) => void;
 type DidCloseCallback = (params: DidCloseTextDocumentParams) => void;
@@ -20,10 +19,8 @@ export interface IDocumentEvents {
   on(event: "save", listener: DidSaveCallback): this;
 }
 
-@injectable()
 export class DocumentEvents extends EventEmitter implements IDocumentEvents {
-  constructor() {
-    const connection = container.resolve<IConnection>("Connection");
+  constructor(connection: Connection) {
     super();
 
     connection.onDidChangeTextDocument((e) => this.emit("change", e));
diff --git a/src/util/elmWorkspaceMatcher.ts b/src/util/elmWorkspaceMatcher.ts
index a735b43..ae74f5e 100644
--- a/src/util/elmWorkspaceMatcher.ts
+++ b/src/util/elmWorkspaceMatcher.ts
@@ -1,4 +1,3 @@
-import { container } from "tsyringe";
 import { URI } from "vscode-uri";
 import { IElmWorkspace } from "../elmWorkspace";
 import { NoWorkspaceContainsError } from "./noWorkspaceContainsError";
@@ -9,11 +8,10 @@ import { NoWorkspaceContainsError } from "./noWorkspaceContainsError";
  * (handlerForWorkspace).
  */
 export class ElmWorkspaceMatcher<ParamType> {
-  private elmWorkspaces: IElmWorkspace[];
-
-  constructor(private getUriFor: (param: ParamType) => URI) {
-    this.elmWorkspaces = container.resolve("ElmWorkspaces");
-  }
+  constructor(
+    protected readonly elmWorkspaces: IElmWorkspace[],
+    protected readonly getUriFor: (param: ParamType) => URI,
+  ) {}
 
   public handlerForWorkspace<ResultType>(
     handler: (param: ParamType, elmWorkspace: IElmWorkspace) => ResultType,
diff --git a/src/util/importUtils.ts b/src/util/importUtils.ts
index 6a444e9..5ee1368 100644
--- a/src/util/importUtils.ts
+++ b/src/util/importUtils.ts
@@ -27,6 +27,11 @@ export class ImportUtils {
         currentTree,
       );
 
+      const importedModules =
+        TreeUtils.findAllImportClauseNodes(currentTree)?.map(
+          (n) => TreeUtils.findFirstNamedChildOfType("upper_case_qid", n)?.text,
+        ) ?? [];
+
       // Filter out already imported values
       // Then sort by startsWith filter text, then matches filter text
       return this.getPossibleImports(forest, uri)
@@ -62,7 +67,16 @@ export class ImportUtils {
             } else if (!aMatches && bMatches) {
               return 1;
             } else {
-              return 0;
+              const aModuleImported = importedModules.includes(a.module);
+              const bModuleImported = importedModules.includes(b.module);
+
+              if (aModuleImported && !bModuleImported) {
+                return -1;
+              } else if (!aModuleImported && bModuleImported) {
+                return 1;
+              } else {
+                return 0;
+              }
             }
           }
         });
diff --git a/src/util/settings.ts b/src/util/settings.ts
index 983ad9f..6e430cc 100644
--- a/src/util/settings.ts
+++ b/src/util/settings.ts
@@ -1,5 +1,4 @@
 import { ClientCapabilities, IConnection } from "vscode-languageserver";
-import { injectable, container } from "tsyringe";
 
 export interface IClientSettings {
   elmFormatPath: string;
@@ -17,7 +16,6 @@ export interface IExtendedCapabilites {
 
 export type ElmAnalyseTrigger = "change" | "save" | "never";
 
-@injectable()
 export class Settings {
   private clientSettings: IClientSettings = {
     elmAnalyseTrigger: "change",
@@ -26,12 +24,14 @@ export class Settings {
     elmTestPath: "",
     trace: { server: "off" },
   };
-  private connection: IConnection;
 
   private initDone = false;
 
-  constructor(config: any, private clientCapabilities: ClientCapabilities) {
-    this.connection = container.resolve<IConnection>("Connection");
+  constructor(
+    private connection: IConnection,
+    config: any,
+    private clientCapabilities: ClientCapabilities,
+  ) {
     this.updateSettings(config);
   }
 
diff --git a/src/util/textDocumentEvents.ts b/src/util/textDocumentEvents.ts
index 97ee805..c700fa4 100644
--- a/src/util/textDocumentEvents.ts
+++ b/src/util/textDocumentEvents.ts
@@ -1,5 +1,4 @@
 import { EventEmitter } from "events";
-import { container } from "tsyringe";
 import {
   DidChangeTextDocumentParams,
   DidCloseTextDocumentParams,
@@ -7,22 +6,22 @@ import {
   DidSaveTextDocumentParams,
   TextDocumentsConfiguration,
 } from "vscode-languageserver";
-import { TextDocument } from "vscode-languageserver-textdocument";
 import { IDocumentEvents } from "./documentEvents";
 
 // This is loosely based on https://github.com/Microsoft/vscode-languageserver-node/blob/73180893ca/server/src/main.ts#L124
 // With some simplifications and the ability to support multiple listeners
-export class TextDocumentEvents extends EventEmitter {
+export class TextDocumentEvents<T> extends EventEmitter {
   // a single store of documents shared by all workspaces
-  private _documents: { [uri: string]: TextDocument };
-  private _configuration: TextDocumentsConfiguration<
-    TextDocument
-  > = TextDocument;
+  private _documents: { [uri: string]: T };
+  private _configuration: TextDocumentsConfiguration<T>;
 
-  constructor() {
+  constructor(
+    configuration: TextDocumentsConfiguration<T>,
+    events: IDocumentEvents,
+  ) {
     super();
-    const events = container.resolve<IDocumentEvents>("DocumentEvents");
     this._documents = Object.create(null);
+    this._configuration = configuration;
 
     events.on("open", (params: DidOpenTextDocumentParams) => {
       const td = params.textDocument;
@@ -82,7 +81,7 @@ export class TextDocumentEvents extends EventEmitter {
    * @param uri The text document's URI to retrieve.
    * @return the text document or `undefined`.
    */
-  public get(uri: string): TextDocument | undefined {
+  public get(uri: string): T | undefined {
     return this._documents[uri];
   }
 }
diff --git a/src/util/treeUtils.ts b/src/util/treeUtils.ts
index b07b44a..19e847d 100644
--- a/src/util/treeUtils.ts
+++ b/src/util/treeUtils.ts
@@ -856,19 +856,33 @@ export class TreeUtils {
 
       let definitionFromOtherFile;
       if (!definitionNode) {
-        definitionFromOtherFile = this.findImportFromImportList(
-          uri,
-          TreeUtils.findImportNameNode(tree, upperCaseQid.text)?.text ??
-            upperCaseQid.text,
-          "Module",
-          imports,
-        );
-        if (definitionFromOtherFile) {
-          return {
-            node: definitionFromOtherFile.node,
-            nodeType: "Module",
-            uri: definitionFromOtherFile.fromUri,
-          };
+        // Make sure the next node is a dot, or else it isn't a Module
+        if (TreeUtils.nextNode(nodeAtPosition)?.type === "dot") {
+          const endPos =
+            upperCaseQid.text.indexOf(nodeAtPosition.text) +
+            nodeAtPosition.text.length;
+
+          const moduleNameOrAlias = nodeAtPosition.parent.text.substring(
+            0,
+            endPos,
+          );
+          const moduleName =
+            TreeUtils.findImportNameNode(tree, moduleNameOrAlias)?.text ??
+            moduleNameOrAlias;
+
+          definitionFromOtherFile = this.findImportFromImportList(
+            uri,
+            moduleName,
+            "Module",
+            imports,
+          );
+          if (definitionFromOtherFile) {
+            return {
+              node: definitionFromOtherFile.node,
+              nodeType: "Module",
+              uri: definitionFromOtherFile.fromUri,
+            };
+          }
         }
 
         definitionFromOtherFile = this.findImportFromImportList(
@@ -1367,7 +1381,7 @@ export class TreeUtils {
     tree: Tree,
     moduleName: string,
   ): SyntaxNode | undefined {
-    const allImports = this.findAllImportNameNodes(tree);
+    const allImports = this.findAllImportClauseNodes(tree);
     if (allImports) {
       return allImports.find(
         (a) =>
@@ -1382,7 +1396,7 @@ export class TreeUtils {
     forest: IForest,
     tree: Tree,
   ): { module: string; value: string }[] {
-    const allImports = this.findAllImportNameNodes(tree);
+    const allImports = TreeUtils.findAllImportClauseNodes(tree);
 
     const allImportedValues: { module: string; value: string }[] = [];
 
@@ -1441,7 +1455,7 @@ export class TreeUtils {
     tree: Tree,
     moduleName: string,
   ): SyntaxNode | undefined {
-    const allImports = this.findAllImportNameNodes(tree);
+    const allImports = this.findAllImportClauseNodes(tree);
     if (allImports) {
       const match = allImports.find(
         (a) =>
@@ -1933,7 +1947,7 @@ export class TreeUtils {
   }
 
   public static getLastImportNode(tree: Tree): SyntaxNode | undefined {
-    const allImportNodes = this.findAllImportNameNodes(tree);
+    const allImportNodes = this.findAllImportClauseNodes(tree);
     if (allImportNodes?.length) {
       return allImportNodes[allImportNodes.length - 1];
     }
@@ -2029,7 +2043,7 @@ export class TreeUtils {
       );
   }
 
-  private static findAllImportNameNodes(tree: Tree): SyntaxNode[] | undefined {
+  public static findAllImportClauseNodes(tree: Tree): SyntaxNode[] | undefined {
     const result = tree.rootNode.children.filter(
       (a) => a.type === "import_clause",
     );
@@ -2050,4 +2064,22 @@ export class TreeUtils {
       node.parent?.parent?.firstNamedChild?.type === "import"
     );
   }
+
+  public static nextNode(node: SyntaxNode): SyntaxNode | undefined {
+    // Move up until we have a sibling
+    while (!node.nextNamedSibling && node.parent) {
+      node = node.parent;
+    }
+
+    if (node.nextNamedSibling) {
+      node = node.nextNamedSibling;
+
+      // Move down the leftmost subtree
+      while (node.firstNamedChild) {
+        node = node.firstNamedChild;
+      }
+
+      return node;
+    }
+  }
 }
diff --git a/tsconfig.json b/tsconfig.json
index b5a556e..b12c40b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -11,9 +11,7 @@
     "lib": ["es2019"],
     "resolveJsonModule": true,
     "esModuleInterop": true,
-    "composite": true,
-    "experimentalDecorators": true,
-    "emitDecoratorMetadata": true
+    "composite": true
   },
   "include": ["src"],
   "exclude": ["node_modules", ".vscode-test", "**/*.test.ts"]


Test patch:
diff --git a/test/completionProvider.test.ts b/test/completionProvider.test.ts
index a32c78e..6f7d2cc 100644
--- a/test/completionProvider.test.ts
+++ b/test/completionProvider.test.ts
@@ -1,17 +1,19 @@
-import { isDeepStrictEqual } from "util";
+import { CompletionProvider, CompletionResult } from "../src/providers";
 import {
+  CompletionParams,
   CompletionContext,
+  IConnection,
   CompletionItem,
-  CompletionParams,
   Position,
   TextEdit,
 } from "vscode-languageserver";
-import { URI } from "vscode-uri";
 import { IElmWorkspace } from "../src/elmWorkspace";
-import { CompletionProvider, CompletionResult } from "../src/providers";
+import { SourceTreeParser } from "./utils/sourceTreeParser";
 import { baseUri } from "./utils/mockElmWorkspace";
+import { mockDeep } from "jest-mock-extended";
 import { getCaretPositionFromSource } from "./utils/sourceParser";
-import { SourceTreeParser } from "./utils/sourceTreeParser";
+import { URI } from "vscode-uri";
+import { isDeepStrictEqual } from "util";
 
 class MockCompletionProvider extends CompletionProvider {
   public handleCompletion(
@@ -26,7 +28,9 @@ type exactCompletions = "exactMatch" | "partialMatch";
 type dotCompletions = "triggeredByDot" | "normal";
 
 describe("CompletionProvider", () => {
-  const completionProvider = new MockCompletionProvider();
+  const connectionMock = mockDeep<IConnection>();
+
+  const completionProvider = new MockCompletionProvider(connectionMock, []);
   const treeParser = new SourceTreeParser();
 
   const debug = process.argv.find((arg) => arg === "--debug");
diff --git a/test/definitionProviderTests/definitionProviderTestBase.ts b/test/definitionProviderTests/definitionProviderTestBase.ts
index 861aee0..e19729c 100644
--- a/test/definitionProviderTests/definitionProviderTestBase.ts
+++ b/test/definitionProviderTests/definitionProviderTestBase.ts
@@ -1,11 +1,16 @@
-import { Location, TextDocumentPositionParams } from "vscode-languageserver";
-import { URI } from "vscode-uri";
-import { IElmWorkspace } from "../../src/elmWorkspace";
 import { DefinitionProvider, DefinitionResult } from "../../src/providers";
-import { TreeUtils } from "../../src/util/treeUtils";
+import {
+  IConnection,
+  TextDocumentPositionParams,
+  Location,
+} from "vscode-languageserver";
+import { IElmWorkspace } from "../../src/elmWorkspace";
+import { SourceTreeParser } from "../utils/sourceTreeParser";
 import { baseUri } from "../utils/mockElmWorkspace";
+import { mockDeep } from "jest-mock-extended";
+import { TreeUtils } from "../../src/util/treeUtils";
 import { getInvokeAndTargetPositionFromSource } from "../utils/sourceParser";
-import { SourceTreeParser } from "../utils/sourceTreeParser";
+import { URI } from "vscode-uri";
 
 class MockDefinitionProvider extends DefinitionProvider {
   public handleDefinition(
@@ -17,10 +22,16 @@ class MockDefinitionProvider extends DefinitionProvider {
 }
 
 export class DefinitionProviderTestBase {
+  private connectionMock: IConnection;
   private definitionProvider: MockDefinitionProvider;
   private treeParser: SourceTreeParser;
   constructor() {
-    this.definitionProvider = new MockDefinitionProvider();
+    this.connectionMock = mockDeep<IConnection>();
+
+    this.definitionProvider = new MockDefinitionProvider(
+      this.connectionMock,
+      [],
+    );
     this.treeParser = new SourceTreeParser();
   }
 
@@ -65,6 +76,30 @@ export class DefinitionProviderTestBase {
           expect((definition as Location).uri).toContain(
             determinedTestType.targetFile,
           );
+
+          if (determinedTestType.targetPosition) {
+            const rootNode = this.treeParser
+              .getWorkspace(determinedTestType.sources)
+              .getForest()
+              .treeIndex.find((a) => a.uri === targetUri)!.tree.rootNode;
+            const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
+              rootNode,
+              determinedTestType.targetPosition,
+            );
+
+            expect((definition as Location).range).toEqual(
+              expect.objectContaining({
+                start: {
+                  line: determinedTestType.targetPosition.line,
+                  character: nodeAtPosition.startPosition.column,
+                },
+                end: {
+                  line: expect.any(Number),
+                  character: expect.any(Number),
+                },
+              }),
+            );
+          }
         }
         break;
 
diff --git a/test/definitionProviderTests/moduleResolveDefinition.test.ts b/test/definitionProviderTests/moduleResolveDefinition.test.ts
new file mode 100644
index 0000000..9bf728b
--- /dev/null
+++ b/test/definitionProviderTests/moduleResolveDefinition.test.ts
@@ -0,0 +1,166 @@
+import { DefinitionProviderTestBase } from "./definitionProviderTestBase";
+
+describe("moduleResolveDefinition", () => {
+  const testBase = new DefinitionProviderTestBase();
+
+  it(`conflicting module and types`, async () => {
+    const source = `
+--@ Model.elm
+module Model exposing (..)
+
+type alias Model = {
+--X
+  var: String
+}
+
+--@ Main.elm
+module Main exposing (..)
+
+import Model exposing (Model)
+     
+func : Model
+        --^Model.elm
+`;
+    await testBase.testDefinition(source);
+
+    const source2 = `
+--@ Model.elm
+module Model exposing (..)
+--X
+
+type alias Model = {
+  var: String
+}
+
+--@ Main.elm
+module Main exposing (..)
+
+import Model exposing (Model)
+      
+func = Model.
+        --^Model.elm
+`;
+    await testBase.testDefinition(source2);
+  });
+
+  it(`conflicting module alias and types`, async () => {
+    const source = `
+--@ Module/Model.elm
+module Module.Model exposing (..)
+
+type alias Model = {
+--X
+  var: String
+}
+
+--@ Main.elm
+module Main exposing (..)
+
+import Module.Model as Model exposing (Model)
+     
+func : Model
+        --^Module/Model.elm
+`;
+    await testBase.testDefinition(source);
+
+    const source2 = `
+--@ Module/Model.elm
+module Module.Model exposing (..)
+--X
+
+type alias Model = {
+  var: String
+}
+
+--@ Main.elm
+module Main exposing (..)
+
+import Module.Model as Model exposing (Model)
+      
+func = Model.
+        --^Module/Model.elm
+`;
+    await testBase.testDefinition(source2);
+  });
+
+  it(`module and submodule definitions`, async () => {
+    const source = `
+--@ Module/Model.elm
+module Module.Model exposing (..)
+--X
+
+type alias Model = {
+  var: String
+}
+
+--@ Module.elm
+module Module exposing (..)
+
+type alias Model = {
+  var: String
+}
+
+--@ Main.elm
+module Main exposing (..)
+
+import Module.Model
+     
+func = Module.Model.func
+             --^Module/Model.elm
+`;
+    await testBase.testDefinition(source);
+
+    const source2 = `
+--@ Module/Model.elm
+module Module.Model exposing (..)
+
+type alias Model = {
+  var: String
+}
+
+--@ Module.elm
+module Module exposing (..)
+--X
+
+type alias Model = {
+  var: String
+}
+
+--@ Main.elm
+module Main exposing (..)
+
+import Module
+import Module.Model
+      
+func = Module.Model.func
+      --^Module.elm
+`;
+    await testBase.testDefinition(source2);
+  });
+
+  it(`module and submodule definitions`, async () => {
+    const source = `
+--@ Module.elm
+module Module exposing (..)
+--X
+
+type alias Model = {
+  var: String
+}
+
+--@ Main.elm
+module Main exposing (..)
+
+import Module as Model
+     
+func = 
+  let
+    Model.
+    --^Module.elm
+  
+  in
+  []
+`;
+    await testBase.testDefinition(source);
+  });
+});
diff --git a/test/jest.setup.ts b/test/jest.setup.ts
deleted file mode 100644
index edb9519..0000000
--- a/test/jest.setup.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import "reflect-metadata";
-import { container } from "tsyringe";
-import { IConnection } from "vscode-languageserver";
-import { mockDeep } from "jest-mock-extended";
-
-container.register("Connection", { useValue: mockDeep<IConnection>() });
-container.register("ElmWorkspaces", { useValue: [] });
diff --git a/test/utils/mockElmWorkspace.ts b/test/utils/mockElmWorkspace.ts
index aeade6b..3f1e079 100644
--- a/test/utils/mockElmWorkspace.ts
+++ b/test/utils/mockElmWorkspace.ts
@@ -4,7 +4,6 @@ import Parser, { Tree } from "web-tree-sitter";
 import { IElmWorkspace } from "../../src/elmWorkspace";
 import { Forest, IForest } from "../../src/forest";
 import { Imports } from "../../src/imports";
-import { container } from "tsyringe";
 
 export const baseUri = Path.join(__dirname, "../sources/src/");
 
@@ -13,9 +12,9 @@ export class MockElmWorkspace implements IElmWorkspace {
   private forest: IForest = new Forest();
   private parser: Parser;
 
-  constructor(sources: { [K: string]: string }) {
-    this.parser = container.resolve("Parser");
-    this.imports = new Imports();
+  constructor(sources: { [K: string]: string }, parser: Parser) {
+    this.parser = parser;
+    this.imports = new Imports(parser);
 
     for (const key in sources) {
       if (Object.prototype.hasOwnProperty.call(sources, key)) {
diff --git a/test/utils/sourceParser.ts b/test/utils/sourceParser.ts
index bca5479..09af05f 100644
--- a/test/utils/sourceParser.ts
+++ b/test/utils/sourceParser.ts
@@ -59,6 +59,7 @@ interface IResolvesToDifferentFileTest {
   targetFile: string;
   sources: { [K: string]: string };
   fileWithTarget: string;
+  targetPosition?: Position;
 }
 
 export function getInvokeAndTargetPositionFromSource(source: string): TestType {
@@ -125,6 +126,7 @@ export function getInvokeAndTargetPositionFromSource(source: string): TestType {
       sources,
       targetFile,
       fileWithTarget,
+      targetPosition,
     };
   } else {
     if (!invokePosition || !targetPosition) {
diff --git a/test/utils/sourceTreeParser.ts b/test/utils/sourceTreeParser.ts
index 222dde9..3031bd6 100644
--- a/test/utils/sourceTreeParser.ts
+++ b/test/utils/sourceTreeParser.ts
@@ -2,7 +2,6 @@ import * as Path from "path";
 import Parser from "web-tree-sitter";
 import { IElmWorkspace } from "../../src/elmWorkspace";
 import { MockElmWorkspace } from "./mockElmWorkspace";
-import { container } from "tsyringe";
 
 export class SourceTreeParser {
   private parser?: Parser;
@@ -17,11 +16,11 @@ export class SourceTreeParser {
     const pathToWasm = Path.relative(process.cwd(), absolute);
 
     const language = await Parser.Language.load(pathToWasm);
-    container.registerSingleton("Parser", Parser);
-    container.resolve<Parser>("Parser").setLanguage(language);
+    this.parser = new Parser();
+    this.parser.setLanguage(language);
   }
 
   public getWorkspace(sources: { [K: string]: string }): IElmWorkspace {
-    return new MockElmWorkspace(sources);
+    return new MockElmWorkspace(sources, this.parser!);
   }
 }