Repository: elm-tooling/elm-language-server
Pull request #388 (2020-12-06 01:33:21)

Typed diagnostic sources


Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index e0ec846..ecb61c4 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -31,7 +31,7 @@ module.exports = {
     "@typescript-eslint/no-misused-promises": "warn",
     "@typescript-eslint/unbound-method": "warn",
     "@typescript-eslint/no-var-requires": "warn",
-    "@typescript-eslint/no-use-before-define": "warn",
+    "@typescript-eslint/no-use-before-define":"off",
     "@typescript-eslint/no-explicit-any": "warn",
     "@typescript-eslint/no-non-null-assertion": "warn",
     "@typescript-eslint/explicit-function-return-type": "warn",
diff --git a/src/cancellation.ts b/src/cancellation.ts
index 1005721..01b1173 100644
--- a/src/cancellation.ts
+++ b/src/cancellation.ts
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
 import * as fs from "fs";
 import * as os from "os";
 import path from "path";
diff --git a/src/elmWorkspace.ts b/src/elmWorkspace.ts
index d1f016d..7480ba4 100644
--- a/src/elmWorkspace.ts
+++ b/src/elmWorkspace.ts
@@ -529,7 +529,7 @@ export class ElmWorkspace implements IElmWorkspace {
         encoding: "utf-8",
       });
 
-      const tree: Tree | undefined = this.parser.parse(fileContent);
+      const tree: Tree = this.parser.parse(fileContent);
       this.forest.setTree(
         URI.file(filePath.path).toString(),
         filePath.writeable,
diff --git a/src/protocol.ts b/src/protocol.ts
index c997596..b2f92f8 100644
--- a/src/protocol.ts
+++ b/src/protocol.ts
@@ -1,5 +1,6 @@
 import { CodeActionParams, RequestType } from "vscode-languageserver";
 import { URI } from "vscode-uri";
+import { IParams } from "./util/elmWorkspaceMatcher";
 
 // eslint-disable-next-line @typescript-eslint/no-namespace
 export const GetMoveDestinationRequest = new RequestType<
@@ -10,7 +11,7 @@ export const GetMoveDestinationRequest = new RequestType<
 
 export const MoveRequest = new RequestType<IMoveParams, void, void>("elm/move");
 
-export interface IMoveParams {
+export interface IMoveParams extends IParams {
   sourceUri: string;
   params: CodeActionParams;
   destination?: IMoveDestination;
@@ -30,7 +31,7 @@ export const ExposeRequest = new RequestType<IExposeUnexposeParams, void, void>(
   "elm/expose",
 );
 
-export interface IExposeUnexposeParams {
+export interface IExposeUnexposeParams extends IParams {
   uri: string;
   name: string;
 }
diff --git a/src/providers/astProvider.ts b/src/providers/astProvider.ts
index 4b21256..bdac1cd 100644
--- a/src/providers/astProvider.ts
+++ b/src/providers/astProvider.ts
@@ -10,13 +10,16 @@ import {
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import Parser, { Tree, Edit, Point, SyntaxNode } from "web-tree-sitter";
-import { IElmWorkspace } from "../elmWorkspace";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { Position, Range } from "vscode-languageserver-textdocument";
 import { FileEventsHandler } from "./handlers/fileEventsHandler";
 import { TextDocumentEvents } from "../util/textDocumentEvents";
 import { TreeUtils } from "../util/treeUtils";
 import { ITreeContainer } from "../forest";
+import {
+  IDidChangeTextDocumentParams,
+  IDidOpenTextDocumentParams,
+} from "./paramsExtensions";
 
 export class ASTProvider {
   private connection: Connection;
@@ -43,28 +46,27 @@ export class ASTProvider {
       "change",
       new ElmWorkspaceMatcher((params: DidChangeTextDocumentParams) =>
         URI.parse(params.textDocument.uri),
-      ).handlerForWorkspace(this.handleChangeTextDocument),
+      ).handle(this.handleChangeTextDocument.bind(this)),
     );
 
     this.documentEvents.on(
       "open",
       new ElmWorkspaceMatcher((params: DidOpenTextDocumentParams) =>
         URI.parse(params.textDocument.uri),
-      ).handlerForWorkspace(this.handleChangeTextDocument),
+      ).handle(this.handleChangeTextDocument.bind(this)),
     );
   }
 
   protected handleChangeTextDocument = (
-    params: DidChangeTextDocumentParams | DidOpenTextDocumentParams,
-    elmWorkspace: IElmWorkspace,
+    params: IDidChangeTextDocumentParams | IDidOpenTextDocumentParams,
   ): void => {
     this.connection.console.info(
       `Changed text document, going to parse it. ${params.textDocument.uri}`,
     );
-    const forest = elmWorkspace.getForest(false); // Don't synchronize the forest, we are only looking at the tree
+    const forest = params.program.getForest(false); // Don't synchronize the forest, we are only looking at the tree
     const document: VersionedTextDocumentIdentifier = params.textDocument;
 
-    let tree: Tree | undefined = forest.getTree(document.uri);
+    let tree: Tree = params.sourceFile.tree;
 
     if ("contentChanges" in params) {
       for (const change of params.contentChanges) {
@@ -104,9 +106,9 @@ export class ASTProvider {
           TreeUtils.getTypeAnnotation(startNode)
         ) {
           changedDeclaration = startNode;
-          elmWorkspace.getTypeCache().invalidateValueDeclaration(startNode);
+          params.program.getTypeCache().invalidateValueDeclaration(startNode);
         } else {
-          elmWorkspace.getTypeCache().invalidateProject();
+          params.program.getTypeCache().invalidateProject();
         }
       });
 
@@ -122,7 +124,7 @@ export class ASTProvider {
       );
 
       // The workspace now needs to be synchronized
-      elmWorkspace.markAsDirty();
+      params.program.markAsDirty();
 
       setImmediate(() => {
         if (tree) {
diff --git a/src/providers/codeAction/addTypeAnnotationCodeAction.ts b/src/providers/codeAction/addTypeAnnotationCodeAction.ts
index 28a40a2..da8502d 100644
--- a/src/providers/codeAction/addTypeAnnotationCodeAction.ts
+++ b/src/providers/codeAction/addTypeAnnotationCodeAction.ts
@@ -1,8 +1,8 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
 import { CodeAction, Range, TextEdit } from "vscode-languageserver";
 import { TreeUtils } from "../../util/treeUtils";
 import { Diagnostics } from "../../util/types/diagnostics";
-import { CodeActionProvider, ICodeActionParams } from "../codeActionProvider";
+import { CodeActionProvider } from "../codeActionProvider";
+import { ICodeActionParams } from "../paramsExtensions";
 
 const errorCodes = [Diagnostics.MissingTypeAnnotation.code];
 const fixId = "add_type_annotation";
diff --git a/src/providers/codeAction/importCodeAction.ts b/src/providers/codeAction/importCodeAction.ts
index 59ef5fe..09bed6b 100644
--- a/src/providers/codeAction/importCodeAction.ts
+++ b/src/providers/codeAction/importCodeAction.ts
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
 import { CodeAction, TextEdit } from "vscode-languageserver";
 import { Range } from "vscode-languageserver-textdocument";
 import { SyntaxNode } from "web-tree-sitter";
@@ -7,7 +6,8 @@ import { ImportUtils, IPossibleImport } from "../../util/importUtils";
 import { RefactorEditUtils } from "../../util/refactorEditUtils";
 import { TreeUtils } from "../../util/treeUtils";
 import { Diagnostics } from "../../util/types/diagnostics";
-import { CodeActionProvider, ICodeActionParams } from "../codeActionProvider";
+import { CodeActionProvider } from "../codeActionProvider";
+import { ICodeActionParams } from "../paramsExtensions";
 
 const errorCodes = [Diagnostics.MissingValue.code];
 const fixId = "import";
diff --git a/src/providers/codeAction/makeDeclarationFromUsageCodeAction.ts b/src/providers/codeAction/makeDeclarationFromUsageCodeAction.ts
index 9307a9c..63cf19d 100644
--- a/src/providers/codeAction/makeDeclarationFromUsageCodeAction.ts
+++ b/src/providers/codeAction/makeDeclarationFromUsageCodeAction.ts
@@ -1,10 +1,10 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
 import { CodeAction, Range } from "vscode-languageserver";
 import { TextEdit } from "vscode-languageserver-textdocument";
 import { RefactorEditUtils } from "../../util/refactorEditUtils";
 import { TreeUtils } from "../../util/treeUtils";
 import { Diagnostics } from "../../util/types/diagnostics";
-import { CodeActionProvider, ICodeActionParams } from "../codeActionProvider";
+import { CodeActionProvider } from "../codeActionProvider";
+import { ICodeActionParams } from "../paramsExtensions";
 
 const errorCodes = [Diagnostics.MissingValue.code];
 const fixId = "make_declaration_from_usage";
diff --git a/src/providers/codeActionProvider.ts b/src/providers/codeActionProvider.ts
index b6ff4aa..d32c7c2 100644
--- a/src/providers/codeActionProvider.ts
+++ b/src/providers/codeActionProvider.ts
@@ -9,7 +9,7 @@ import {
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import { SyntaxNode, Tree } from "web-tree-sitter";
-import { ElmWorkspaceMatcher, IParams } from "../util/elmWorkspaceMatcher";
+import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { MultiMap } from "../util/multiMap";
 import { RefactorEditUtils } from "../util/refactorEditUtils";
 import { Settings } from "../util/settings";
@@ -24,8 +24,7 @@ import { ElmMakeDiagnostics } from "./diagnostics/elmMakeDiagnostics";
 import { diagnosticsEquals } from "./diagnostics/fileDiagnostics";
 import { ExposeUnexposeHandler } from "./handlers/exposeUnexposeHandler";
 import { MoveRefactoringHandler } from "./handlers/moveRefactoringHandler";
-
-export type ICodeActionParams = CodeActionParams & IParams;
+import { ICodeActionParams } from "./paramsExtensions";
 
 export interface ICodeActionRegistration {
   errorCodes: string[];
@@ -206,8 +205,7 @@ export class CodeActionProvider {
     // Top level annotation are handled by diagnostics
     const codeActions: CodeAction[] = [];
 
-    const forest = params.program.getForest();
-    const treeContainer = forest.getByUri(params.textDocument.uri);
+    const treeContainer = params.sourceFile;
     const tree = treeContainer?.tree;
     const checker = params.program.getTypeChecker();
 
diff --git a/src/providers/codeLensProvider.ts b/src/providers/codeLensProvider.ts
index 427b671..09fdc31 100644
--- a/src/providers/codeLensProvider.ts
+++ b/src/providers/codeLensProvider.ts
@@ -10,11 +10,11 @@ import {
 } 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 { References } from "../util/references";
 import { Settings } from "../util/settings";
 import { TreeUtils } from "../util/treeUtils";
+import { ICodeLens, ICodeLensParams } from "./paramsExtensions";
 
 type CodeLensType = "exposed" | "referenceCounter";
 type CodeLensResult = CodeLens[] | null | undefined;
@@ -29,45 +29,40 @@ export class CodeLensProvider {
     this.connection.onCodeLens(
       new ElmWorkspaceMatcher((param: CodeLensParams) =>
         URI.parse(param.textDocument.uri),
-      ).handlerForWorkspace(this.handleCodeLensRequest),
+      ).handle(this.handleCodeLensRequest),
     );
     this.connection.onCodeLensResolve(
       new ElmWorkspaceMatcher((param: CodeLens) =>
         URI.parse(param.data.uri),
-      ).handlerForWorkspace(this.handleCodeLensResolveRequest),
+      ).handle(this.handleCodeLensResolveRequest),
     );
   }
 
   protected handleCodeLensRequest = (
-    param: CodeLensParams,
-    elmWorkspace: IElmWorkspace,
+    param: ICodeLensParams,
   ): CodeLensResult => {
     this.connection.console.info(
       `A code lens was requested for ${param.textDocument.uri}`,
     );
     const codeLens: CodeLens[] = [];
 
-    const forest = elmWorkspace.getForest();
-    const tree: Tree | undefined = forest.getTree(param.textDocument.uri);
+    const tree: Tree = param.sourceFile.tree;
 
-    if (tree) {
-      codeLens.push(
-        ...this.getExposingCodeLenses(tree, param.textDocument.uri),
-      );
+    codeLens.push(...this.getExposingCodeLenses(tree, param.textDocument.uri));
 
-      codeLens.push(
-        ...this.getReferencesCodeLenses(tree, param.textDocument.uri),
-      );
+    codeLens.push(
+      ...this.getReferencesCodeLenses(tree, param.textDocument.uri),
+    );
 
-      return codeLens;
-    }
+    return codeLens;
   };
 
-  protected handleCodeLensResolveRequest = (
-    param: CodeLens,
-    elmWorkspace: IElmWorkspace,
-  ): CodeLens => {
-    const codelens = param;
+  protected handleCodeLensResolveRequest = (param: ICodeLens): CodeLens => {
+    const codelens: CodeLens = {
+      range: param.range,
+      command: param.command,
+      data: param.data,
+    };
     const data: {
       codeLensType: CodeLensType;
       references: Location[];
@@ -78,9 +73,8 @@ export class CodeLensProvider {
     this.connection.console.info(
       `A code lens resolve was requested for ${data.uri}`,
     );
-    const forest = elmWorkspace.getForest();
-    const checker = elmWorkspace.getTypeChecker();
-    const treeContainer = forest.getByUri(data.uri);
+    const checker = param.program.getTypeChecker();
+    const treeContainer = param.sourceFile;
     if (treeContainer && data.codeLensType) {
       const tree = treeContainer.tree;
 
@@ -116,7 +110,7 @@ export class CodeLensProvider {
             treeContainer,
           );
 
-          const references = References.find(definitionNode, elmWorkspace);
+          const references = References.find(definitionNode, param.program);
 
           let refLocations: Location[] = [];
           if (references) {
diff --git a/src/providers/completionProvider.ts b/src/providers/completionProvider.ts
index 1eafcab..18b4358 100644
--- a/src/providers/completionProvider.ts
+++ b/src/providers/completionProvider.ts
@@ -27,6 +27,7 @@ import { DiagnosticsProvider } from ".";
 import { TypeChecker } from "../util/types/typeChecker";
 import escapeStringRegexp from "escape-string-regexp";
 import { TRecord } from "../util/types/typeInference";
+import { ICompletionParams } from "./paramsExtensions";
 
 export type CompletionResult =
   | CompletionItem[]
@@ -53,25 +54,24 @@ export class CompletionProvider {
   constructor() {
     this.connection = container.resolve<Connection>("Connection");
     this.diagnostics = container.resolve(DiagnosticsProvider);
-    this.connection.onCompletion((params) =>
+    this.connection.onCompletion(
       this.diagnostics.interruptDiagnostics(() =>
         new ElmWorkspaceMatcher((params: CompletionParams) =>
           URI.parse(params.textDocument.uri),
-        ).handlerForWorkspace(this.handleCompletionRequest)(params),
+        ).handle(this.handleCompletionRequest.bind(this)),
       ),
     );
   }
 
   protected handleCompletionRequest = (
-    params: CompletionParams,
-    elmWorkspace: IElmWorkspace,
+    params: ICompletionParams,
   ): CompletionResult => {
     this.connection.console.info(`A completion was requested`);
     const completions: CompletionItem[] = [];
 
-    const forest = elmWorkspace.getForest();
-    const checker = elmWorkspace.getTypeChecker();
-    const treeContainer = forest.getByUri(params.textDocument.uri);
+    const forest = params.program.getForest();
+    const checker = params.program.getTypeChecker();
+    const treeContainer = params.sourceFile;
 
     if (treeContainer) {
       const tree = treeContainer?.tree;
@@ -274,7 +274,7 @@ export class CompletionProvider {
           nodeAtPosition,
           treeContainer,
           replaceRange,
-          elmWorkspace,
+          params.program,
         );
       }
 
@@ -327,7 +327,7 @@ export class CompletionProvider {
         const moduleCompletions = this.getSubmodulesOrValues(
           targetNode,
           treeContainer,
-          elmWorkspace,
+          params.program,
           replaceRange,
           targetWord,
         );
@@ -340,7 +340,7 @@ export class CompletionProvider {
           targetNode,
           treeContainer,
           replaceRange,
-          elmWorkspace,
+          params.program,
         );
 
         if (recordCompletions.length > 0) {
@@ -375,7 +375,7 @@ export class CompletionProvider {
       completions.push(...this.getKeywordsInline());
 
       const possibleImportCompletions = this.getPossibleImports(
-        elmWorkspace,
+        params.program,
         replaceRange,
         tree,
         params.textDocument.uri,
diff --git a/src/providers/definitionProvider.ts b/src/providers/definitionProvider.ts
index a7f3464..a070b6c 100644
--- a/src/providers/definitionProvider.ts
+++ b/src/providers/definitionProvider.ts
@@ -8,10 +8,10 @@ import {
   TextDocumentPositionParams,
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
-import { SyntaxNode, Tree } from "web-tree-sitter";
-import { IElmWorkspace } from "../elmWorkspace";
+import { SyntaxNode } from "web-tree-sitter";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { TreeUtils } from "../util/treeUtils";
+import { ITextDocumentPositionParams } from "./paramsExtensions";
 
 export type DefinitionResult =
   | Location
@@ -27,18 +27,16 @@ export class DefinitionProvider {
     this.connection.onDefinition(
       new ElmWorkspaceMatcher((param: TextDocumentPositionParams) =>
         URI.parse(param.textDocument.uri),
-      ).handlerForWorkspace(this.handleDefinitionRequest),
+      ).handle(this.handleDefinitionRequest.bind(this)),
     );
   }
 
   protected handleDefinitionRequest = (
-    param: TextDocumentPositionParams,
-    elmWorkspace: IElmWorkspace,
+    param: ITextDocumentPositionParams,
   ): DefinitionResult => {
     this.connection.console.info(`A definition was requested`);
-    const forest = elmWorkspace.getForest();
-    const checker = elmWorkspace.getTypeChecker();
-    const treeContainer = forest.getByUri(param.textDocument.uri);
+    const checker = param.program.getTypeChecker();
+    const treeContainer = param.sourceFile;
 
     if (treeContainer) {
       const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
diff --git a/src/providers/diagnostics/diagnosticSource.ts b/src/providers/diagnostics/diagnosticSource.ts
new file mode 100644
index 0000000..3080c56
--- /dev/null
+++ b/src/providers/diagnostics/diagnosticSource.ts
@@ -0,0 +1,1 @@
+export type DiagnosticSource = "Elm" | "ElmLS";
diff --git a/src/providers/diagnostics/diagnosticsProvider.ts b/src/providers/diagnostics/diagnosticsProvider.ts
index f71ec6e..5ed75a2 100644
--- a/src/providers/diagnostics/diagnosticsProvider.ts
+++ b/src/providers/diagnostics/diagnosticsProvider.ts
@@ -17,6 +17,7 @@ import { IClientSettings } from "../../util/settings";
 import { TextDocumentEvents } from "../../util/textDocumentEvents";
 import { Diagnostic } from "../../util/types/diagnostics";
 import { ASTProvider } from "../astProvider";
+import { DiagnosticSource } from "./diagnosticSource";
 import { DiagnosticsRequest } from "./diagnosticsRequest";
 import { ElmLsDiagnostics } from "./elmLsDiagnostics";
 import { ElmMakeDiagnostics } from "./elmMakeDiagnostics";
@@ -38,6 +39,7 @@ export interface IElmIssue {
 }
 
 export interface IDiagnostic extends Omit<LspDiagnostic, "code"> {
+  source: DiagnosticSource;
   data: {
     uri: string;
     code: string;
diff --git a/src/providers/diagnostics/elmLsDiagnostics.ts b/src/providers/diagnostics/elmLsDiagnostics.ts
index 7d9a94e..632bff2 100644
--- a/src/providers/diagnostics/elmLsDiagnostics.ts
+++ b/src/providers/diagnostics/elmLsDiagnostics.ts
@@ -21,8 +21,6 @@ import { Utils } from "../../util/utils";
 import { IDiagnostic } from "./diagnosticsProvider";
 
 export class ElmLsDiagnostics {
-  ELMLS = "ElmLS";
-
   private language: Language;
   private elmWorkspaceMatcher: ElmWorkspaceMatcher<URI>;
 
@@ -361,7 +359,7 @@ export class ElmLsDiagnostics {
   public onCodeAction(params: CodeActionParams): CodeAction[] {
     const { uri } = params.textDocument;
     const elmDiagnostics: IDiagnostic[] = params.context.diagnostics.filter(
-      (diagnostic) => diagnostic.source === this.ELMLS,
+      (diagnostic) => diagnostic.source === "ElmLS",
     ) as IDiagnostic[];
 
     return this.convertDiagnosticsToCodeActions(elmDiagnostics, uri);
@@ -498,7 +496,7 @@ export class ElmLsDiagnostics {
           range: this.getNodeRange(moduleImport.parent),
           message: `Unused import \`${moduleImport.text}\``,
           severity: DiagnosticSeverity.Warning,
-          source: this.ELMLS,
+          source: "ElmLS",
           tags: [DiagnosticTag.Unnecessary],
           data: { uri: tree.uri, code: "unused_import" },
         });
@@ -546,7 +544,7 @@ export class ElmLsDiagnostics {
             exposedValueOrType.type === "exposed_type" ? "type" : "value"
           } \`${exposedValueOrType.text}\``,
           severity: DiagnosticSeverity.Warning,
-          source: this.ELMLS,
+          source: "ElmLS",
           tags: [DiagnosticTag.Unnecessary],
           data: { uri: tree.uri, code: "unused_imported_value" },
         });
@@ -576,7 +574,7 @@ export class ElmLsDiagnostics {
           range: this.getNodeRange(moduleAlias.parent),
           message: `Unused import alias \`${moduleAlias.text}\``,
           severity: DiagnosticSeverity.Warning,
-          source: this.ELMLS,
+          source: "ElmLS",
           tags: [DiagnosticTag.Unnecessary],
           data: { uri: tree.uri, code: "unused_alias" },
         });
@@ -657,7 +655,7 @@ export class ElmLsDiagnostics {
               range: this.getNodeRange(pattern),
               message: `Unused top level definition \`${pattern.text}\``,
               severity: DiagnosticSeverity.Warning,
-              source: this.ELMLS,
+              source: "ElmLS",
               tags: [DiagnosticTag.Unnecessary],
               data: { uri: tree.uri, code: "unused_top_level" },
             });
@@ -668,7 +666,7 @@ export class ElmLsDiagnostics {
               range: this.getNodeRange(pattern),
               message: `Unused pattern variable \`${pattern.text}\``,
               severity: DiagnosticSeverity.Warning,
-              source: this.ELMLS,
+              source: "ElmLS",
               tags: [DiagnosticTag.Unnecessary],
               data: { uri: tree.uri, code: "unused_pattern" },
             });
@@ -693,7 +691,7 @@ export class ElmLsDiagnostics {
         range: this.getNodeRange(caseBranch),
         message: `\`Nothing\` mapped to \`Nothing\` in case expression. Use Maybe.map or Maybe.andThen instead.`,
         severity: DiagnosticSeverity.Warning,
-        source: this.ELMLS,
+        source: "ElmLS",
         data: { uri: tree.uri, code: "map_nothing_to_nothing" },
       });
     });
@@ -715,7 +713,7 @@ export class ElmLsDiagnostics {
         range: this.getNodeRange(caseExpr),
         message: `Use an if expression instead of a case expression.`,
         severity: DiagnosticSeverity.Warning,
-        source: this.ELMLS,
+        source: "ElmLS",
         data: { uri: tree.uri, code: "boolean_case_expr" },
       });
     });
@@ -739,7 +737,7 @@ export class ElmLsDiagnostics {
         },
         message: `If you concatenate two lists, then you can merge them into one list.`,
         severity: DiagnosticSeverity.Warning,
-        source: this.ELMLS,
+        source: "ElmLS",
         data: { uri: tree.uri, code: "drop_concat_of_lists" },
       });
     });
@@ -763,7 +761,7 @@ export class ElmLsDiagnostics {
         },
         message: `If you cons an item to a literal list, then you can just put the item into the list.`,
         severity: DiagnosticSeverity.Warning,
-        source: this.ELMLS,
+        source: "ElmLS",
         data: { uri: tree.uri, code: "drop_cons_of_item_and_list" },
       });
     });
@@ -787,7 +785,7 @@ export class ElmLsDiagnostics {
         },
         message: `If you concatenate two lists, but the first item is a single element list, then you should use the cons operator.`,
         severity: DiagnosticSeverity.Warning,
-        source: this.ELMLS,
+        source: "ElmLS",
         data: { uri: tree.uri, code: "use_cons_over_concat" },
       });
     });
@@ -830,7 +828,7 @@ export class ElmLsDiagnostics {
           range: this.getNodeRange(recordType),
           message: `Using a record is obsolete if you only plan to store a single field in it.`,
           severity: DiagnosticSeverity.Warning,
-          source: this.ELMLS,
+          source: "ElmLS",
           data: { uri: tree.uri, code: "single_field_record" },
         });
       }
@@ -852,7 +850,7 @@ export class ElmLsDiagnostics {
         range: this.getNodeRange(listConcat),
         message: `You should just merge the arguments of \`List.concat\` to a single list.`,
         severity: DiagnosticSeverity.Warning,
-        source: this.ELMLS,
+        source: "ElmLS",
         data: { uri: tree.uri, code: "unnecessary_list_concat" },
       });
     });
@@ -873,7 +871,7 @@ export class ElmLsDiagnostics {
         range: this.getNodeRange(unusedPortMatches[0].captures[0].node),
         message: `Module is definined as a \`port\` module, but does not define any ports.`,
         severity: DiagnosticSeverity.Warning,
-        source: this.ELMLS,
+        source: "ElmLS",
         data: { uri: tree.uri, code: "unnecessary_port_module" },
       });
     }
@@ -896,7 +894,7 @@ export class ElmLsDiagnostics {
         range: this.getNodeRange(operatorFunction),
         message: `Don't use fully applied prefix notation for operators.`,
         severity: DiagnosticSeverity.Warning,
-        source: this.ELMLS,
+        source: "ElmLS",
         data: { uri: tree.uri, code: "no_uncurried_prefix" },
       });
     });
@@ -937,7 +935,7 @@ export class ElmLsDiagnostics {
           range: this.getNodeRange(typeAlias.parent),
           message: `Type alias \`${typeAlias.text}\` is not used.`,
           severity: DiagnosticSeverity.Warning,
-          source: this.ELMLS,
+          source: "ElmLS",
           tags: [DiagnosticTag.Unnecessary],
           data: { uri: tree.uri, code: "unused_type_alias" },
         });
@@ -987,7 +985,7 @@ export class ElmLsDiagnostics {
           range: this.getNodeRange(unionVariant.parent),
           message: `Value constructor \`${unionVariant.text}\` is not used.`,
           severity: DiagnosticSeverity.Warning,
-          source: this.ELMLS,
+          source: "ElmLS",
           tags: [DiagnosticTag.Unnecessary],
           data: { uri: tree.uri, code: "unused_value_constructor" },
         });
diff --git a/src/providers/documentFormatingProvider.ts b/src/providers/documentFormatingProvider.ts
index 6a017f4..2cf748a 100644
--- a/src/providers/documentFormatingProvider.ts
+++ b/src/providers/documentFormatingProvider.ts
@@ -6,12 +6,12 @@ import {
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import { DiagnosticsProvider } from ".";
-import { IElmWorkspace } from "../elmWorkspace";
 import * as Diff from "../util/diff";
 import { execCmd } from "../util/elmUtils";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { Settings } from "../util/settings";
 import { TextDocumentEvents } from "../util/textDocumentEvents";
+import { IDocumentFormattingParams } from "./paramsExtensions";
 
 type DocumentFormattingResult = Promise<TextEdit[] | undefined>;
 
@@ -27,11 +27,11 @@ export class DocumentFormattingProvider {
     this.connection = container.resolve<Connection>("Connection");
     this.events = container.resolve<TextDocumentEvents>(TextDocumentEvents);
     this.diagnostics = container.resolve(DiagnosticsProvider);
-    this.connection.onDocumentFormatting((params) =>
+    this.connection.onDocumentFormatting(
       this.diagnostics.interruptDiagnostics(() =>
         new ElmWorkspaceMatcher((params: DocumentFormattingParams) =>
           URI.parse(params.textDocument.uri),
-        ).handlerForWorkspace(this.handleFormattingRequest)(params),
+        ).handle(this.handleFormattingRequest),
       ),
     );
   }
@@ -62,8 +62,7 @@ export class DocumentFormattingProvider {
   };
 
   protected handleFormattingRequest = async (
-    params: DocumentFormattingParams,
-    elmWorkspace: IElmWorkspace,
+    params: IDocumentFormattingParams,
   ): DocumentFormattingResult => {
     this.connection.console.info(`Formatting was requested`);
     try {
@@ -75,7 +74,7 @@ export class DocumentFormattingProvider {
 
       const settings = await this.settings.getClientSettings();
       return await this.formatText(
-        elmWorkspace.getRootPath(),
+        params.program.getRootPath(),
         settings.elmFormatPath,
         text.getText(),
       );
diff --git a/src/providers/documentSymbolProvider.ts b/src/providers/documentSymbolProvider.ts
index e0e61cc..9888ec0 100644
--- a/src/providers/documentSymbolProvider.ts
+++ b/src/providers/documentSymbolProvider.ts
@@ -8,10 +8,10 @@ import {
 } 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 { SymbolInformationTranslator } from "../util/symbolTranslator";
 import { ThrottledCancellationToken } from "../cancellation";
+import { IDocumentSymbolParams } from "./paramsExtensions";
 
 type DocumentSymbolResult =
   | SymbolInformation[]
@@ -27,20 +27,18 @@ export class DocumentSymbolProvider {
     this.connection.onDocumentSymbol(
       new ElmWorkspaceMatcher((param: DocumentSymbolParams) =>
         URI.parse(param.textDocument.uri),
-      ).handlerForWorkspace(this.handleDocumentSymbolRequest),
+      ).handle(this.handleDocumentSymbolRequest.bind(this)),
     );
   }
 
   private handleDocumentSymbolRequest = (
-    param: DocumentSymbolParams,
-    elmWorkspace: IElmWorkspace,
+    param: IDocumentSymbolParams,
     token?: CancellationToken,
   ): DocumentSymbolResult => {
     this.connection.console.info(`Document Symbols were requested`);
     const symbolInformationList: SymbolInformation[] = [];
 
-    const forest = elmWorkspace.getForest();
-    const tree: Tree | undefined = forest.getTree(param.textDocument.uri);
+    const tree: Tree = param.sourceFile.tree;
 
     const cancellationToken = token
       ? new ThrottledCancellationToken(token)
@@ -61,9 +59,8 @@ export class DocumentSymbolProvider {
         traverse(childNode);
       }
     };
-    if (tree) {
-      traverse(tree.rootNode);
-    }
+
+    traverse(tree.rootNode);
 
     return symbolInformationList;
   };
diff --git a/src/providers/foldingProvider.ts b/src/providers/foldingProvider.ts
index aa91544..a3d4d45 100644
--- a/src/providers/foldingProvider.ts
+++ b/src/providers/foldingProvider.ts
@@ -7,8 +7,8 @@ import {
 } 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 { IFoldingRangeParams } from "./paramsExtensions";
 
 export class FoldingRangeProvider {
   private readonly REGION_CONSTRUCTS: Set<string> = new Set([
@@ -25,18 +25,16 @@ export class FoldingRangeProvider {
     this.connection.onFoldingRanges(
       new ElmWorkspaceMatcher((param: FoldingRangeParams) =>
         URI.parse(param.textDocument.uri),
-      ).handlerForWorkspace(this.handleFoldingRange),
+      ).handle(this.handleFoldingRange.bind(this)),
     );
   }
 
   protected handleFoldingRange = (
-    param: FoldingRangeParams,
-    elmWorkspace: IElmWorkspace,
+    param: IFoldingRangeParams,
   ): FoldingRange[] => {
     this.connection.console.info(`Folding ranges were requested`);
     const folds: FoldingRange[] = [];
-    const forest = elmWorkspace.getForest();
-    const tree: Tree | undefined = forest.getTree(param.textDocument.uri);
+    const tree: Tree = param.sourceFile.tree;
 
     const findLastIdenticalNamedSibling: (node: SyntaxNode) => SyntaxNode = (
       node: SyntaxNode,
@@ -52,97 +50,95 @@ export class FoldingRangeProvider {
       }
     };
 
-    if (tree) {
-      const treeCursor = tree.walk();
+    const treeCursor = tree.walk();
 
-      const traverse: () => void = (): void => {
-        const node = treeCursor.currentNode();
-        if (node.parent?.lastChild && node.isNamed) {
-          if ("import_clause" === node.type) {
-            const previousSibling = node.previousNamedSibling;
-            if (
-              previousSibling === null ||
-              previousSibling.type !== "import_clause"
-            ) {
-              const lastNode = findLastIdenticalNamedSibling(node);
-              folds.push({
-                endCharacter: lastNode.endPosition.column,
-                endLine: lastNode.endPosition.row,
-                kind: FoldingRangeKind.Imports,
-                startCharacter: node.startPosition.column,
-                startLine: node.startPosition.row,
-              });
-            }
-          } else if (node.type === "let_in_expr") {
-            // Use fields in the future
-            const valueDeclarations = node.namedChildren.filter(
-              (n) => n.type === "value_declaration",
-            );
-            const lastValueDeclaration =
-              valueDeclarations[valueDeclarations.length - 1];
-            const letBody = node.lastNamedChild;
-
-            if (lastValueDeclaration) {
-              folds.push({
-                endCharacter: lastValueDeclaration.endPosition.column,
-                endLine: lastValueDeclaration.endPosition.row,
-                kind: FoldingRangeKind.Region,
-                startCharacter: node.startPosition.column,
-                startLine: node.startPosition.row,
-              });
-            }
-
-            if (letBody) {
-              folds.push({
-                endCharacter: node.endPosition.column,
-                endLine: node.endPosition.row,
-                kind: FoldingRangeKind.Region,
-                startCharacter: letBody.startPosition.column,
-                startLine: letBody.startPosition.row - 1,
-              });
-            }
-          } else if (node.type === "if_else_expr") {
-            node.namedChildren.slice(1).forEach((child) => {
-              folds.push({
-                endCharacter: child.endPosition.column,
-                endLine: child.endPosition.row,
-                kind: FoldingRangeKind.Region,
-                startCharacter: child.startPosition.column,
-                startLine: child.startPosition.row - 1,
-              });
+    const traverse: () => void = (): void => {
+      const node = treeCursor.currentNode();
+      if (node.parent?.lastChild && node.isNamed) {
+        if ("import_clause" === node.type) {
+          const previousSibling = node.previousNamedSibling;
+          if (
+            previousSibling === null ||
+            previousSibling.type !== "import_clause"
+          ) {
+            const lastNode = findLastIdenticalNamedSibling(node);
+            folds.push({
+              endCharacter: lastNode.endPosition.column,
+              endLine: lastNode.endPosition.row,
+              kind: FoldingRangeKind.Imports,
+              startCharacter: node.startPosition.column,
+              startLine: node.startPosition.row,
             });
-          } else if (this.REGION_CONSTRUCTS.has(node.type)) {
+          }
+        } else if (node.type === "let_in_expr") {
+          // Use fields in the future
+          const valueDeclarations = node.namedChildren.filter(
+            (n) => n.type === "value_declaration",
+          );
+          const lastValueDeclaration =
+            valueDeclarations[valueDeclarations.length - 1];
+          const letBody = node.lastNamedChild;
+
+          if (lastValueDeclaration) {
             folds.push({
-              endCharacter: node.endPosition.column,
-              endLine: node.endPosition.row,
+              endCharacter: lastValueDeclaration.endPosition.column,
+              endLine: lastValueDeclaration.endPosition.row,
               kind: FoldingRangeKind.Region,
               startCharacter: node.startPosition.column,
               startLine: node.startPosition.row,
             });
-          } else if ("block_comment" === node.type) {
+          }
+
+          if (letBody) {
             folds.push({
               endCharacter: node.endPosition.column,
               endLine: node.endPosition.row,
-              kind: FoldingRangeKind.Comment,
-              startCharacter: node.startPosition.column,
-              startLine: node.startPosition.row,
+              kind: FoldingRangeKind.Region,
+              startCharacter: letBody.startPosition.column,
+              startLine: letBody.startPosition.row - 1,
             });
           }
+        } else if (node.type === "if_else_expr") {
+          node.namedChildren.slice(1).forEach((child) => {
+            folds.push({
+              endCharacter: child.endPosition.column,
+              endLine: child.endPosition.row,
+              kind: FoldingRangeKind.Region,
+              startCharacter: child.startPosition.column,
+              startLine: child.startPosition.row - 1,
+            });
+          });
+        } else if (this.REGION_CONSTRUCTS.has(node.type)) {
+          folds.push({
+            endCharacter: node.endPosition.column,
+            endLine: node.endPosition.row,
+            kind: FoldingRangeKind.Region,
+            startCharacter: node.startPosition.column,
+            startLine: node.startPosition.row,
+          });
+        } else if ("block_comment" === node.type) {
+          folds.push({
+            endCharacter: node.endPosition.column,
+            endLine: node.endPosition.row,
+            kind: FoldingRangeKind.Comment,
+            startCharacter: node.startPosition.column,
+            startLine: node.startPosition.row,
+          });
         }
+      }
 
-        if (treeCursor.gotoFirstChild()) {
-          traverse();
-
-          while (treeCursor.gotoNextSibling()) {
-            traverse();
-          }
+      if (treeCursor.gotoFirstChild()) {
+        traverse();
 
-          treeCursor.gotoParent();
+        while (treeCursor.gotoNextSibling()) {
+          traverse();
         }
-      };
 
-      traverse();
-    }
+        treeCursor.gotoParent();
+      }
+    };
+
+    traverse();
 
     this.connection.console.info(`Returned ${folds.length} folding ranges`);
     return folds;
diff --git a/src/providers/handlers/exposeUnexposeHandler.ts b/src/providers/handlers/exposeUnexposeHandler.ts
index 2d0d455..510a4cc 100644
--- a/src/providers/handlers/exposeUnexposeHandler.ts
+++ b/src/providers/handlers/exposeUnexposeHandler.ts
@@ -1,7 +1,6 @@
 import { container } from "tsyringe";
 import { Connection } from "vscode-languageserver";
 import { URI } from "vscode-uri";
-import { IElmWorkspace } from "../../elmWorkspace";
 import {
   ExposeRequest,
   IExposeUnexposeParams,
@@ -19,23 +18,21 @@ export class ExposeUnexposeHandler {
       ExposeRequest,
       new ElmWorkspaceMatcher((params: IExposeUnexposeParams) =>
         URI.parse(params.uri),
-      ).handlerForWorkspace(this.handleExposeRequest.bind(this)),
+      ).handle(this.handleExposeRequest.bind(this)),
     );
 
     this.connection.onRequest(
       UnexposeRequest,
       new ElmWorkspaceMatcher((params: IExposeUnexposeParams) =>
         URI.parse(params.uri),
-      ).handlerForWorkspace(this.handleUnexposeRequest.bind(this)),
+      ).handle(this.handleUnexposeRequest.bind(this)),
     );
   }
 
   private async handleExposeRequest(
     params: IExposeUnexposeParams,
-    elmWorkspace: IElmWorkspace,
   ): Promise<void> {
-    const forest = elmWorkspace.getForest();
-    const tree = forest.getTree(params.uri);
+    const tree = params.sourceFile.tree;
 
     if (tree) {
       const edits = RefactorEditUtils.exposeValueInModule(tree, params.name);
@@ -52,10 +49,8 @@ export class ExposeUnexposeHandler {
 
   private async handleUnexposeRequest(
     params: IExposeUnexposeParams,
-    elmWorkspace: IElmWorkspace,
   ): Promise<void> {
-    const forest = elmWorkspace.getForest();
-    const tree = forest.getTree(params.uri);
+    const tree = params.sourceFile.tree;
 
     if (tree) {
       const edits = RefactorEditUtils.unexposedValueInModule(tree, params.name);
diff --git a/src/providers/handlers/fileEventsHandler.ts b/src/providers/handlers/fileEventsHandler.ts
index 5d933cb..f505be8 100644
--- a/src/providers/handlers/fileEventsHandler.ts
+++ b/src/providers/handlers/fileEventsHandler.ts
@@ -7,7 +7,7 @@ import {
 import { ElmWorkspaceMatcher } from "../../util/elmWorkspaceMatcher";
 import { RefactorEditUtils } from "../../util/refactorEditUtils";
 import { container } from "tsyringe";
-import { CancellationToken, Connection } from "vscode-languageserver";
+import { Connection } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import { RenameUtils } from "../../util/renameUtils";
 import { RenameProvider } from "../renameProvider";
diff --git a/src/providers/handlers/moveRefactoringHandler.ts b/src/providers/handlers/moveRefactoringHandler.ts
index f1af94b..38ee8c1 100644
--- a/src/providers/handlers/moveRefactoringHandler.ts
+++ b/src/providers/handlers/moveRefactoringHandler.ts
@@ -1,7 +1,6 @@
 import { container } from "tsyringe";
 import { Connection, Position, Range, TextEdit } from "vscode-languageserver";
 import { URI } from "vscode-uri";
-import { IElmWorkspace } from "../../elmWorkspace";
 import {
   GetMoveDestinationRequest,
   IMoveDestination,
@@ -23,28 +22,27 @@ export class MoveRefactoringHandler {
       GetMoveDestinationRequest,
       new ElmWorkspaceMatcher((param: IMoveParams) =>
         URI.parse(param.sourceUri),
-      ).handlerForWorkspace(this.handleGetMoveDestinationsRequest.bind(this)),
+      ).handle(this.handleGetMoveDestinationsRequest.bind(this)),
     );
 
     this.connection.onRequest(
       MoveRequest,
       new ElmWorkspaceMatcher((param: IMoveParams) =>
         URI.parse(param.sourceUri),
-      ).handlerForWorkspace(this.handleMoveRequest.bind(this)),
+      ).handle(this.handleMoveRequest.bind(this)),
     );
   }
 
   private handleGetMoveDestinationsRequest(
     params: IMoveParams,
-    elmWorkspace: IElmWorkspace,
   ): IMoveDestinationsResponse {
-    const forest = elmWorkspace.getForest();
+    const forest = params.program.getForest();
 
     const destinations: IMoveDestination[] = Array.from(forest.treeMap.values())
       .filter((tree) => tree.writeable && tree.uri !== params.sourceUri)
       .map((tree) => {
         let uri = URI.parse(tree.uri).fsPath;
-        const rootPath = elmWorkspace.getRootPath().fsPath;
+        const rootPath = params.program.getRootPath().fsPath;
 
         uri = uri.slice(rootPath.length + 1);
         const index = uri.lastIndexOf("\\");
@@ -61,15 +59,12 @@ export class MoveRefactoringHandler {
     };
   }
 
-  private async handleMoveRequest(
-    params: IMoveParams,
-    elmWorkspace: IElmWorkspace,
-  ): Promise<void> {
+  private async handleMoveRequest(params: IMoveParams): Promise<void> {
     if (!params.destination) {
       return;
     }
 
-    const forest = elmWorkspace.getForest();
+    const forest = params.program.getForest();
     const tree = forest.getTree(params.sourceUri);
     const destinationTree = forest.getTree(params.destination.uri);
 
@@ -159,7 +154,7 @@ export class MoveRefactoringHandler {
             nodeType: "Function",
             uri: params.sourceUri,
           },
-          elmWorkspace,
+          params.program,
         ).map((ref) => {
           return {
             ...ref,
diff --git a/src/providers/hoverProvider.ts b/src/providers/hoverProvider.ts
index b775697..1d1cbd1 100644
--- a/src/providers/hoverProvider.ts
+++ b/src/providers/hoverProvider.ts
@@ -8,11 +8,11 @@ import {
 import { URI } from "vscode-uri";
 import { SyntaxNode } from "web-tree-sitter";
 import { DiagnosticsProvider } from ".";
-import { IElmWorkspace } from "../elmWorkspace";
 import { getEmptyTypes } from "../util/elmUtils";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { HintHelper } from "../util/hintHelper";
 import { NodeType, TreeUtils } from "../util/treeUtils";
+import { ITextDocumentPositionParams } from "./paramsExtensions";
 
 type HoverResult = Hover | null | undefined;
 
@@ -23,24 +23,22 @@ export class HoverProvider {
   constructor() {
     this.connection = container.resolve<Connection>("Connection");
     this.diagnostics = container.resolve(DiagnosticsProvider);
-    this.connection.onHover((params) =>
+    this.connection.onHover(
       this.diagnostics.interruptDiagnostics(() =>
         new ElmWorkspaceMatcher((params: TextDocumentPositionParams) =>
           URI.parse(params.textDocument.uri),
-        ).handlerForWorkspace(this.handleHoverRequest)(params),
+        ).handle(this.handleHoverRequest.bind(this)),
       ),
     );
   }
 
   protected handleHoverRequest = (
-    params: TextDocumentPositionParams,
-    elmWorkspace: IElmWorkspace,
+    params: ITextDocumentPositionParams,
   ): HoverResult => {
     this.connection.console.info(`A hover was requested`);
 
-    const forest = elmWorkspace.getForest();
-    const checker = elmWorkspace.getTypeChecker();
-    const treeContainer = forest.getByUri(params.textDocument.uri);
+    const checker = params.program.getTypeChecker();
+    const treeContainer = params.sourceFile;
 
     if (treeContainer) {
       const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
diff --git a/src/providers/paramsExtensions.ts b/src/providers/paramsExtensions.ts
new file mode 100644
index 0000000..0dc1ada
--- /dev/null
+++ b/src/providers/paramsExtensions.ts
@@ -0,0 +1,33 @@
+import {
+  CodeActionParams,
+  CodeLens,
+  CodeLensParams,
+  CompletionParams,
+  DidChangeTextDocumentParams,
+  DidOpenTextDocumentParams,
+  DocumentFormattingParams,
+  DocumentSymbolParams,
+  FoldingRangeParams,
+  PrepareRenameParams,
+  ReferenceParams,
+  RenameParams,
+  SelectionRangeParams,
+  TextDocumentPositionParams,
+} from "vscode-languageserver";
+import { IParams } from "../util/elmWorkspaceMatcher";
+
+export type ITextDocumentPositionParams = TextDocumentPositionParams & IParams;
+export type ICodeActionParams = CodeActionParams & IParams;
+export type ICompletionParams = CompletionParams & IParams;
+export type IDocumentSymbolParams = DocumentSymbolParams & IParams;
+export type IFoldingRangeParams = FoldingRangeParams & IParams;
+export type IReferenceParams = ReferenceParams & IParams;
+export type IPrepareRenameParams = PrepareRenameParams & IParams;
+export type IRenameParams = RenameParams & IParams;
+export type ISelectionRangeParams = SelectionRangeParams & IParams;
+export type ICodeLensParams = CodeLensParams & IParams;
+export type ICodeLens = CodeLens & IParams;
+export type IDocumentFormattingParams = DocumentFormattingParams & IParams;
+export type IDidChangeTextDocumentParams = DidChangeTextDocumentParams &
+  IParams;
+export type IDidOpenTextDocumentParams = DidOpenTextDocumentParams & IParams;
diff --git a/src/providers/referencesProvider.ts b/src/providers/referencesProvider.ts
index fb9a2f4..c1ec973 100644
--- a/src/providers/referencesProvider.ts
+++ b/src/providers/referencesProvider.ts
@@ -7,10 +7,10 @@ import {
   ReferenceParams,
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
-import { IElmWorkspace } from "../elmWorkspace";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { References } from "../util/references";
 import { TreeUtils } from "../util/treeUtils";
+import { IReferenceParams } from "./paramsExtensions";
 
 export type ReferenceResult = Location[] | null | undefined;
 
@@ -21,20 +21,18 @@ export class ReferencesProvider {
     this.connection.onReferences(
       new ElmWorkspaceMatcher((param: ReferenceParams) =>
         URI.parse(param.textDocument.uri),
-      ).handlerForWorkspace(this.handleReferencesRequest),
+      ).handle(this.handleReferencesRequest.bind(this)),
     );
   }
 
   protected handleReferencesRequest = (
-    params: ReferenceParams,
-    elmWorkspace: IElmWorkspace,
+    params: IReferenceParams,
   ): ReferenceResult => {
     this.connection.console.info(`References were requested`);
 
-    const forest = elmWorkspace.getForest();
-    const checker = elmWorkspace.getTypeChecker();
+    const checker = params.program.getTypeChecker();
 
-    const treeContainer = forest.getByUri(params.textDocument.uri);
+    const treeContainer = params.sourceFile;
 
     if (treeContainer) {
       const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
@@ -47,7 +45,7 @@ export class ReferencesProvider {
         treeContainer,
       );
 
-      const references = References.find(definitionNode, elmWorkspace);
+      const references = References.find(definitionNode, params.program);
 
       if (references) {
         return references.map((a) =>
diff --git a/src/providers/renameProvider.ts b/src/providers/renameProvider.ts
index 71e304f..66a6b66 100644
--- a/src/providers/renameProvider.ts
+++ b/src/providers/renameProvider.ts
@@ -16,6 +16,7 @@ import { SyntaxNode } from "web-tree-sitter";
 import { IElmWorkspace } from "../elmWorkspace";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { RenameUtils } from "../util/renameUtils";
+import { IRenameParams, IPrepareRenameParams } from "./paramsExtensions";
 
 export class RenameProvider {
   private connection: Connection;
@@ -25,26 +26,25 @@ export class RenameProvider {
     this.connection.onPrepareRename(
       new ElmWorkspaceMatcher((params: PrepareRenameParams) =>
         URI.parse(params.textDocument.uri),
-      ).handlerForWorkspace(this.handlePrepareRenameRequest),
+      ).handle(this.handlePrepareRenameRequest.bind(this)),
     );
 
     this.connection.onRenameRequest(
       new ElmWorkspaceMatcher((params: RenameParams) =>
         URI.parse(params.textDocument.uri),
-      ).handlerForWorkspace(this.handleRenameRequest),
+      ).handle(this.handleRenameRequest.bind(this)),
     );
   }
 
   protected handleRenameRequest = (
-    params: RenameParams,
-    elmWorkspace: IElmWorkspace,
+    params: IRenameParams,
   ): WorkspaceEdit | null | undefined => {
     this.connection.console.info(`Renaming was requested`);
 
     let newName = params.newName;
 
     const affectedNodes = RenameUtils.getRenameAffectedNodes(
-      elmWorkspace,
+      params.program,
       params.textDocument.uri,
       params.position,
     );
@@ -54,7 +54,7 @@ export class RenameProvider {
     const renameChanges: RenameFile[] = [];
     const moduleDeclarationRenameChange = this.createModuleDeclarationRenameChange(
       affectedNodes,
-      elmWorkspace,
+      params.program,
       params,
       newName,
     );
@@ -74,13 +74,12 @@ export class RenameProvider {
   };
 
   protected handlePrepareRenameRequest = (
-    params: PrepareRenameParams,
-    elmWorkspace: IElmWorkspace,
+    params: IPrepareRenameParams,
   ): Range | null => {
     this.connection.console.info(`Prepare rename was requested`);
 
     const affectedNodes = RenameUtils.getRenameAffectedNodes(
-      elmWorkspace,
+      params.program,
       params.textDocument.uri,
       params.position,
     );
diff --git a/src/providers/selectionRangeProvider.ts b/src/providers/selectionRangeProvider.ts
index 9ebea8a..bcc038c 100644
--- a/src/providers/selectionRangeProvider.ts
+++ b/src/providers/selectionRangeProvider.ts
@@ -8,10 +8,10 @@ import {
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import { SyntaxNode, Tree } from "web-tree-sitter";
-import { IElmWorkspace } from "../elmWorkspace";
 import { PositionUtil } from "../positionUtil";
 import { ElmWorkspaceMatcher } from "../util/elmWorkspaceMatcher";
 import { TreeUtils } from "../util/treeUtils";
+import { ISelectionRangeParams } from "./paramsExtensions";
 
 export class SelectionRangeProvider {
   private connection: Connection;
@@ -21,43 +21,39 @@ export class SelectionRangeProvider {
     this.connection.onSelectionRanges(
       new ElmWorkspaceMatcher((param: SelectionRangeParams) =>
         URI.parse(param.textDocument.uri),
-      ).handlerForWorkspace(this.handleSelectionRangeRequest),
+      ).handle(this.handleSelectionRangeRequest.bind(this)),
     );
   }
 
   private handleSelectionRangeRequest = (
-    params: SelectionRangeParams,
-    elmWorkspace: IElmWorkspace,
+    params: ISelectionRangeParams,
   ): SelectionRange[] | null => {
     this.connection.console.info(`Selection Ranges were requested`);
 
     const ret: SelectionRange[] = [];
 
-    const forest = elmWorkspace.getForest();
-    const tree: Tree | undefined = forest.getTree(params.textDocument.uri);
+    const tree: Tree = params.sourceFile.tree;
 
-    if (tree) {
-      params.positions.forEach((position: Position) => {
-        const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
-          tree.rootNode,
-          position,
-        );
+    params.positions.forEach((position: Position) => {
+      const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
+        tree.rootNode,
+        position,
+      );
 
-        const newRange = {
-          start: PositionUtil.FROM_TS_POSITION(
-            nodeAtPosition.startPosition,
-          ).toVSPosition(),
-          end: PositionUtil.FROM_TS_POSITION(
-            nodeAtPosition.endPosition,
-          ).toVSPosition(),
-        };
+      const newRange = {
+        start: PositionUtil.FROM_TS_POSITION(
+          nodeAtPosition.startPosition,
+        ).toVSPosition(),
+        end: PositionUtil.FROM_TS_POSITION(
+          nodeAtPosition.endPosition,
+        ).toVSPosition(),
+      };
 
-        ret.push({
-          range: newRange,
-          parent: this.getParentNode(nodeAtPosition, newRange),
-        });
+      ret.push({
+        range: newRange,
+        parent: this.getParentNode(nodeAtPosition, newRange),
       });
-    }
+    });
 
     return ret ? ret : null;
   };
diff --git a/src/util/types/binder.ts b/src/util/types/binder.ts
index 43837f2..31f03b9 100644
--- a/src/util/types/binder.ts
+++ b/src/util/types/binder.ts
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
 import { ITreeContainer } from "../../forest";
 import { SyntaxNode } from "web-tree-sitter";
 import { MultiMap } from "../multiMap";
diff --git a/src/util/types/diagnostics.ts b/src/util/types/diagnostics.ts
index 89a9c0e..fd25654 100644
--- a/src/util/types/diagnostics.ts
+++ b/src/util/types/diagnostics.ts
@@ -2,11 +2,12 @@
 import { DiagnosticSeverity, Range } from "vscode-languageserver";
 import { SyntaxNode } from "web-tree-sitter";
 import { PositionUtil } from "../../positionUtil";
+import { DiagnosticSource } from "../../providers/diagnostics/diagnosticSource";
 
 export interface Diagnostic {
   code: string;
   message: string;
-  source: string;
+  source: DiagnosticSource;
   severity: DiagnosticSeverity;
   range: Range;
   uri: string;
@@ -32,7 +33,7 @@ export function errorWithEndNode(
     message: format(diagnostic.message, ...args),
     code: diagnostic.code,
     severity: diagnostic.severity,
-    source: "elm",
+    source: "Elm",
     uri: node.tree.uri,
   };
 }
diff --git a/src/util/types/typeChecker.ts b/src/util/types/typeChecker.ts
index 3a74109..9b57be4 100644
--- a/src/util/types/typeChecker.ts
+++ b/src/util/types/typeChecker.ts
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
 /* eslint-disable @typescript-eslint/naming-convention */
 import { SyntaxNode } from "web-tree-sitter";
 import { flatMap, NodeType, TreeUtils } from "../treeUtils";
diff --git a/src/util/utils.ts b/src/util/utils.ts
index c666520..c038c72 100644
--- a/src/util/utils.ts
+++ b/src/util/utils.ts
@@ -1,6 +1,4 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
 import { Range } from "vscode-languageserver";
-import { Diagnostic } from "./types/diagnostics";
 
 export class Utils {
   public static notUndefined<T>(x: T | undefined): x is T {


Test patch:
diff --git a/test/codeActionProvider.test.ts b/test/codeActionProvider.test.ts
index 545eab8..d6753dd 100644
--- a/test/codeActionProvider.test.ts
+++ b/test/codeActionProvider.test.ts
@@ -5,8 +5,8 @@ import { IElmWorkspace } from "../src/elmWorkspace";
 import {
   CodeActionProvider,
   convertFromAnalyzerDiagnostic,
-  ICodeActionParams,
 } from "../src/providers";
+import { ICodeActionParams } from "../src/providers/paramsExtensions";
 import { Utils } from "../src/util/utils";
 import { baseUri } from "./utils/mockElmWorkspace";
 import { getTargetPositionFromSource } from "./utils/sourceParser";
diff --git a/test/completionProvider.test.ts b/test/completionProvider.test.ts
index 0fcfdeb..84764e4 100644
--- a/test/completionProvider.test.ts
+++ b/test/completionProvider.test.ts
@@ -2,23 +2,19 @@ import { isDeepStrictEqual } from "util";
 import {
   CompletionContext,
   CompletionItem,
-  CompletionParams,
   Position,
   TextEdit,
 } from "vscode-languageserver";
 import { URI } from "vscode-uri";
-import { IElmWorkspace } from "../src/elmWorkspace";
 import { CompletionProvider, CompletionResult } from "../src/providers";
+import { ICompletionParams } from "../src/providers/paramsExtensions";
 import { baseUri } from "./utils/mockElmWorkspace";
 import { getCaretPositionFromSource } from "./utils/sourceParser";
 import { SourceTreeParser } from "./utils/sourceTreeParser";
 
 class MockCompletionProvider extends CompletionProvider {
-  public handleCompletion(
-    params: CompletionParams,
-    elmWorkspace: IElmWorkspace,
-  ): CompletionResult {
-    return this.handleCompletionRequest(params, elmWorkspace);
+  public handleCompletion(params: ICompletionParams): CompletionResult {
+    return this.handleCompletionRequest(params);
   }
 }
 
@@ -52,19 +48,24 @@ describe("CompletionProvider", () => {
     );
 
     if (!position) {
-      fail();
+      throw new Error("Getting position failed");
     }
 
-    function testCompletionsWithContext(context: CompletionContext) {
+    const testUri = URI.file(baseUri + fileWithCaret).toString();
+    const program = treeParser.getWorkspace(newSources);
+    const sourceFile = program.getForest().getByUri(testUri);
+
+    function testCompletionsWithContext(context: CompletionContext): void {
+      if (!sourceFile) throw new Error("Getting tree failed");
+
       const completions =
-        completionProvider.handleCompletion(
-          {
-            textDocument: { uri: URI.file(baseUri + fileWithCaret).toString() },
-            position: position!,
-            context,
-          },
-          treeParser.getWorkspace(newSources),
-        ) ?? [];
+        completionProvider.handleCompletion({
+          textDocument: { uri: testUri },
+          position: position!,
+          context,
+          program,
+          sourceFile,
+        }) ?? [];
 
       const completionsList = Array.isArray(completions)
         ? completions
diff --git a/test/definitionProviderTests/definitionProviderTestBase.ts b/test/definitionProviderTests/definitionProviderTestBase.ts
index 62ee77d..da61321 100644
--- a/test/definitionProviderTests/definitionProviderTestBase.ts
+++ b/test/definitionProviderTests/definitionProviderTestBase.ts
@@ -1,7 +1,7 @@
-import { Location, TextDocumentPositionParams } from "vscode-languageserver";
+import { Location } from "vscode-languageserver";
 import { URI } from "vscode-uri";
-import { IElmWorkspace } from "../../src/elmWorkspace";
 import { DefinitionProvider, DefinitionResult } from "../../src/providers";
+import { ITextDocumentPositionParams } from "../../src/providers/paramsExtensions";
 import { TreeUtils } from "../../src/util/treeUtils";
 import { baseUri } from "../utils/mockElmWorkspace";
 import { getInvokeAndTargetPositionFromSource } from "../utils/sourceParser";
@@ -9,10 +9,9 @@ import { SourceTreeParser } from "../utils/sourceTreeParser";
 
 class MockDefinitionProvider extends DefinitionProvider {
   public handleDefinition(
-    params: TextDocumentPositionParams,
-    elmWorkspace: IElmWorkspace,
+    params: ITextDocumentPositionParams,
   ): DefinitionResult {
-    return this.handleDefinitionRequest(params, elmWorkspace);
+    return this.handleDefinitionRequest(params);
   }
 }
 
@@ -32,18 +31,22 @@ export class DefinitionProviderTestBase {
       baseUri + determinedTestType.invokeFile,
     ).toString();
 
+    const program = this.treeParser.getWorkspace(determinedTestType.sources);
+    const sourceFile = program.getForest().getByUri(invokeUri);
+
+    if (!sourceFile) throw new Error("Getting tree failed");
+
     switch (determinedTestType.kind) {
       case "unresolved":
         {
-          const definition = this.definitionProvider.handleDefinition(
-            {
-              textDocument: {
-                uri: invokeUri,
-              },
-              position: determinedTestType.invokePosition,
+          const definition = this.definitionProvider.handleDefinition({
+            textDocument: {
+              uri: invokeUri,
             },
-            this.treeParser.getWorkspace(determinedTestType.sources),
-          );
+            position: determinedTestType.invokePosition,
+            program,
+            sourceFile,
+          });
 
           expect(definition).toEqual(undefined);
         }
@@ -51,18 +54,14 @@ export class DefinitionProviderTestBase {
 
       case "resolvesToDifferentFile":
         {
-          const workspace = this.treeParser.getWorkspace(
-            determinedTestType.sources,
-          );
-          const definition = this.definitionProvider.handleDefinition(
-            {
-              textDocument: {
-                uri: invokeUri,
-              },
-              position: determinedTestType.invokePosition,
+          const definition = this.definitionProvider.handleDefinition({
+            textDocument: {
+              uri: invokeUri,
             },
-            workspace,
-          );
+            position: determinedTestType.invokePosition,
+            program,
+            sourceFile,
+          });
 
           expect(definition).toBeDefined();
           expect((definition as Location).uri).toContain(
@@ -74,8 +73,7 @@ export class DefinitionProviderTestBase {
               baseUri + determinedTestType.targetFile,
             ).toString();
 
-            const rootNode = workspace.getForest().treeMap.get(targetUri)!.tree
-              .rootNode;
+            const rootNode = program.getSourceFile(targetUri)!.tree.rootNode;
             const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
               rootNode,
               determinedTestType.targetPosition,
@@ -99,20 +97,16 @@ export class DefinitionProviderTestBase {
 
       case "resolves":
         {
-          const definition = this.definitionProvider.handleDefinition(
-            {
-              textDocument: {
-                uri: invokeUri,
-              },
-              position: determinedTestType.invokePosition,
+          const definition = this.definitionProvider.handleDefinition({
+            textDocument: {
+              uri: invokeUri,
             },
-            this.treeParser.getWorkspace(determinedTestType.sources),
-          );
+            position: determinedTestType.invokePosition,
+            program,
+            sourceFile,
+          });
 
-          const rootNode = this.treeParser
-            .getWorkspace(determinedTestType.sources)
-            .getForest()
-            .treeMap.get(invokeUri)!.tree.rootNode;
+          const rootNode = program.getSourceFile(invokeUri)!.tree.rootNode;
           const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
             rootNode,
             determinedTestType.targetPosition,
diff --git a/test/diagnosticTests/elmDiagnostics.test.ts b/test/diagnosticTests/elmDiagnostics.test.ts
index 78faef8..98a0590 100644
--- a/test/diagnosticTests/elmDiagnostics.test.ts
+++ b/test/diagnosticTests/elmDiagnostics.test.ts
@@ -1,4 +1,3 @@
-import { debug } from "console";
 import { URI } from "vscode-uri";
 import { SyntaxNode } from "web-tree-sitter";
 import { convertFromAnalyzerDiagnostic } from "../../src/providers";
@@ -88,7 +87,7 @@ describe("test elm diagnostics", () => {
     let nodeAtPosition: SyntaxNode;
 
     if ("position" in result) {
-      const rootNode = program.getForest().treeMap.get(testUri)!.tree.rootNode;
+      const rootNode = program.getSourceFile(testUri)!.tree.rootNode;
       nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
         rootNode,
         result.position,
diff --git a/test/referencesProviderTests/referencesProviderTestBase.ts b/test/referencesProviderTests/referencesProviderTestBase.ts
index bbf4942..c3ba26b 100644
--- a/test/referencesProviderTests/referencesProviderTestBase.ts
+++ b/test/referencesProviderTests/referencesProviderTestBase.ts
@@ -1,18 +1,14 @@
-import { ReferenceParams } from "vscode-languageserver";
 import { URI } from "vscode-uri";
-import { IElmWorkspace } from "../../src/elmWorkspace";
 import { ReferenceResult, ReferencesProvider } from "../../src/providers";
+import { IReferenceParams } from "../../src/providers/paramsExtensions";
 import { TreeUtils } from "../../src/util/treeUtils";
 import { baseUri } from "../utils/mockElmWorkspace";
 import { getReferencesTestFromSource } from "../utils/sourceParser";
 import { SourceTreeParser } from "../utils/sourceTreeParser";
 
 class MockReferencesProvider extends ReferencesProvider {
-  public handleReference(
-    params: ReferenceParams,
-    elmWorkspace: IElmWorkspace,
-  ): ReferenceResult {
-    return this.handleReferencesRequest(params, elmWorkspace);
+  public handleReference(params: IReferenceParams): ReferenceResult {
+    return this.handleReferencesRequest(params);
   }
 }
 
@@ -30,25 +26,30 @@ export class ReferencesProviderTestBase {
     const referenceTest = getReferencesTestFromSource(source);
 
     if (!referenceTest) {
-      fail();
+      throw new Error("Getting references from source failed");
     }
 
+    const testUri = URI.file(baseUri + referenceTest.invokeFile).toString();
+
+    const program = this.treeParser.getWorkspace(referenceTest.sources);
+    const sourceFile = program.getForest().getByUri(testUri);
+
+    if (!sourceFile) throw new Error("Getting tree failed");
+
     const invokeUri = URI.file(baseUri + referenceTest.invokeFile).toString();
 
-    const workspace = this.treeParser.getWorkspace(referenceTest.sources);
     const references =
-      this.referencesProvider.handleReference(
-        {
-          textDocument: {
-            uri: invokeUri,
-          },
-          position: referenceTest.invokePosition,
-          context: {
-            includeDeclaration: true,
-          },
+      this.referencesProvider.handleReference({
+        textDocument: {
+          uri: invokeUri,
+        },
+        position: referenceTest.invokePosition,
+        context: {
+          includeDeclaration: true,
         },
-        workspace,
-      ) ?? [];
+        program,
+        sourceFile,
+      }) ?? [];
 
     // Add invoke position to references
     referenceTest.references.push({
@@ -61,8 +62,7 @@ export class ReferencesProviderTestBase {
     referenceTest.references.forEach(({ referencePosition, referenceFile }) => {
       const referenceUri = URI.file(baseUri + referenceFile).toString();
 
-      const rootNode = workspace.getForest().treeMap.get(referenceUri)!.tree
-        .rootNode;
+      const rootNode = program.getSourceFile(referenceUri)!.tree.rootNode;
       const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
         rootNode,
         referencePosition,
diff --git a/test/utils/mockElmWorkspace.ts b/test/utils/mockElmWorkspace.ts
index 362f976..7e90576 100644
--- a/test/utils/mockElmWorkspace.ts
+++ b/test/utils/mockElmWorkspace.ts
@@ -148,7 +148,7 @@ export class MockElmWorkspace implements IElmWorkspace {
   }
 
   private parseAndAddToForest(fileName: string, source: string): void {
-    const tree: Tree | undefined = this.parser.parse(source);
+    const tree: Tree = this.parser.parse(source);
     this.forest.setTree(
       URI.file(baseUri + fileName).toString(),
       true,