From edbe7bcdacc4c58c08112705af429fe29063719e Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Fri, 3 Dec 2021 15:37:41 +0100 Subject: [PATCH] Get ts-plugin working with Emacs and LSP --- .dir-locals.el | 15 +++++++-- packages/ts-plugin/README.md | 59 ++++++++++++++++++++++++++++++++- packages/ts-plugin/src/index.ts | 16 +++++++-- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/.dir-locals.el b/.dir-locals.el index d08dbd7..798fc99 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -1,6 +1,17 @@ ((typescript-mode - . ((eval . (setq tide-tsserver-executable + . ((eval + . (progn + ;; For TIDE: + (setq tide-tsserver-executable (concat (let ((d (dir-locals-find-file "."))) (if (stringp d) d (car d))) - "node_modules/typescript/lib/tsserver.js")))))) + "node_modules/typescript/lib/tsserver.js")) + ;; For LSP: + (require 'lsp-javascript) + (lsp-dependency 'typescript + `(:system ,(concat + (let ((d (dir-locals-find-file "."))) + (if (stringp d) d (car d))) + "node_modules/typescript/lib/tsserver.js")))) + )))) diff --git a/packages/ts-plugin/README.md b/packages/ts-plugin/README.md index fbe4903..7f19c84 100644 --- a/packages/ts-plugin/README.md +++ b/packages/ts-plugin/README.md @@ -45,7 +45,7 @@ a `typescript/` subdirectory. If no such `typescript/` subdirectory exists in `node_modules`, then because -[tsserver loads plugins from relative to tsserver.js rather than tsconfig.json](https://github.com/microsoft/TypeScript/issues/42688), +[tsserver loads plugins from relative to tsserver.js rather than tsconfig.json][tsserver-plugin-loading-problem], you will have to tell `tide-mode` about where your project's `tsserver` lives. @@ -82,6 +82,52 @@ If you use the `.dir-locals.el` methods, you may need to run `tide-restart-server` once after opening the first TypeScript file in your project (and then close and re-open that TypeScript file). +### Emacs with LSP + +LSP will, by default, use its own `tsserver`, no matter what is in the +local `node_modules`. Because of the [issue with tsserver's approach +to plugin loading][tsserver-plugin-loading-problem], this means that +by default it will not load the Syndicate plugin. + +Overriding the `tsserver` location is similar to the way it's done for +Tide, but instead of a variable value change, a function has to be +called. One good way to do it is to use `.dir-locals.el`, as above: + +```elisp +((typescript-mode + . ((eval . (progn + (require 'lsp-javascript) + (lsp-dependency + 'typescript + `(:system ,(concat + (let ((d (dir-locals-find-file "."))) + (if (stringp d) d (car d))) + "node_modules/typescript/lib/tsserver.js")))))))) +``` + +And, of course, you can make changes for both Tide and LSP at once, if +you like: + +```elisp +((typescript-mode + . ((eval + . (progn + ;; For TIDE: + (setq tide-tsserver-executable + (concat + (let ((d (dir-locals-find-file "."))) + (if (stringp d) d (car d))) + "node_modules/typescript/lib/tsserver.js")) + ;; For LSP: + (require 'lsp-javascript) + (lsp-dependency 'typescript + `(:system ,(concat + (let ((d (dir-locals-find-file "."))) + (if (stringp d) d (car d))) + "node_modules/typescript/lib/tsserver.js")))) + )))) +``` + ### Visual Studio Code After `yarn install`, if you have a `node_modules/typescript` @@ -106,6 +152,8 @@ file containing: ## Debugging +### Emacs with Tide + You can get verbose logs from Tide's tsserver by setting the Emacs variable `tide-tsserver-process-environment`: @@ -125,3 +173,12 @@ Finally, you can set these options in a `.dir-locals.el` file, too: ((typescript-mode . ((tide-tsserver-process-environment . ("TSS_LOG=-level verbose -file /tmp/tss.log"))))) ``` + +### Emacs with LSP + +`lsp-mode` helpfully puts logs in a `.log/` directory in your project +root! So you can follow `.log/tsserver.log`. There are also `lsp-mode` +options for changing the `tsserver` logging level, but I haven't +explored them yet. + +[tsserver-plugin-loading-problem]: https://github.com/microsoft/TypeScript/issues/42688 diff --git a/packages/ts-plugin/src/index.ts b/packages/ts-plugin/src/index.ts index b33ffac..1e30192 100644 --- a/packages/ts-plugin/src/index.ts +++ b/packages/ts-plugin/src/index.ts @@ -50,6 +50,9 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => { return p.firstItem.start.pos + p.offset; } + span(s: ts.TextSpan): ts.TextSpan; + span(s: undefined): undefined; + span(s: ts.TextSpan | undefined): ts.TextSpan | undefined; span(s: ts.TextSpan | undefined): ts.TextSpan | undefined { if (s !== void 0) { const newStart = this.loc(s.start); @@ -485,7 +488,16 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => { } getNavigationTree(fileName: string): ts.NavigationTree { - throw new Error('Method not implemented.'); + const t = this.inner.getNavigationTree(fileName); + return withFileName(fileName, () => t, (fixup) => { + function fixupNavigationTree(t: ts.NavigationTree) { + fixup.span(t.nameSpan); + t.spans.forEach(s => fixup.span(s)); + t.childItems?.forEach(fixupNavigationTree); + } + fixupNavigationTree(t); + return t; + }); } prepareCallHierarchy(fileName: string, position: number): ts.CallHierarchyItem | ts.CallHierarchyItem[] | undefined { @@ -610,7 +622,7 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => { } getApplicableRefactors(fileName: string, positionOrRange: number | ts.TextRange, preferences: ts.UserPreferences | undefined, triggerReason?: ts.RefactorTriggerReason): ts.ApplicableRefactorInfo[] { - throw new Error('Method not implemented.'); + return this.inner.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason); } getEditsForRefactor(fileName: string, formatOptions: ts.FormatCodeSettings, positionOrRange: number | ts.TextRange, refactorName: string, actionName: string, preferences: ts.UserPreferences | undefined): ts.RefactorEditInfo | undefined {