Skip to content
This repository was archived by the owner on Dec 25, 2023. It is now read-only.

Commit 80fb5a6

Browse files
committed
Upgrade to using shared utils
- better dependency management - use full completion result when using suggestions
1 parent cf33065 commit 80fb5a6

File tree

11 files changed

+118
-115
lines changed

11 files changed

+118
-115
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
},
2828
"homepage": "https://github.com/apexskier/nova-typescript",
2929
"devDependencies": {
30+
"@rollup/plugin-commonjs": "^15.1.0",
3031
"@rollup/plugin-node-resolve": "^9.0.0",
3132
"@types/jest": "^26.0.14",
3233
"@types/nova-editor-node": "^1.0.4",
@@ -36,6 +37,7 @@
3637
"eslint": "^7.10.0",
3738
"eslint-plugin-nova": "^1.0.0",
3839
"jest": "^26.4.2",
40+
"nova-extension-utils": "^1.0.2",
3941
"onchange": "^7.0.2",
4042
"prettier": "^2.1.2",
4143
"rollup": "^2.28.2",

rollup.config.main.js

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
11
import typescript from "rollup-plugin-typescript2";
2+
import commonjs from "@rollup/plugin-commonjs";
23
import resolve from "@rollup/plugin-node-resolve";
34

4-
// NOTE: this works, but has a couple issues.
5-
//
6-
// cache issues, such as the following
7-
// ```
8-
// [!] (plugin rpt2) Error: ENOENT: no such file or directory, open '/Users/cameronlittle/Dev/nova-typescript/node_modules/.cache/rollup-plugin-typescript2/rpt2_b2ffe25927fdf3bdfc6af7cc34cbf7c51560889e/code/cache_/dff2aa675eb1f7c9d04145fc75469e189b910ce5'
9-
// ```
10-
//
11-
// it doesn't use the same type resolution as tsc, which has conflicts with @types/node
12-
//
13-
// I'm going to wait until my nova-editor types PR is merged before attempting to fix fully
14-
155
export default {
166
input: "src/main.ts",
17-
plugins: [typescript(), resolve()],
7+
plugins: [typescript(), commonjs(), resolve()],
188
output: {
199
file: "typescript.novaextension/Scripts/main.dist.js",
2010
sourcemap: true,

src/commands/autoSuggest.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// eslint-disable-next-line no-unused-vars
22
import type * as lspTypes from "vscode-languageserver-protocol";
3+
import { asyncNova } from "nova-extension-utils";
34
import { rangeToLspRange, lspRangeToRange } from "../lspNovaConversions";
4-
import { wrapCommand, showChoicePalette } from "../novaUtils";
5+
import { wrapCommand } from "../novaUtils";
56
import { executeCommand } from "./codeAction";
67

78
export function registerAutoSuggest(client: LanguageClient) {
@@ -50,7 +51,7 @@ export function registerAutoSuggest(client: LanguageClient) {
5051
return;
5152
}
5253

53-
const choice = await showChoicePalette(
54+
let choice = await asyncNova.showChoicePalette(
5455
items,
5556
(item) => `${item.label}${item.detail ? `- ${item.detail}` : ""}`,
5657
{ placeholder: "suggestions" }
@@ -59,13 +60,13 @@ export function registerAutoSuggest(client: LanguageClient) {
5960
return;
6061
}
6162

62-
const completionItem = (await client.sendRequest(
63+
choice = (await client.sendRequest(
6364
"completionItem/resolve",
6465
choice
6566
)) as lspTypes.CompletionItem;
6667

6768
if (nova.inDevMode()) {
68-
console.log(JSON.stringify(completionItem, null, " "));
69+
console.log(JSON.stringify(choice, null, " "));
6970
}
7071

7172
const { textEdit, additionalTextEdits, command } = choice;

src/commands/codeAction.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// eslint-disable-next-line no-unused-vars
22
import type * as lspTypes from "vscode-languageserver-protocol";
33
import * as lsp from "vscode-languageserver-types";
4+
import { asyncNova } from "nova-extension-utils";
45
import { applyWorkspaceEdit } from "../applyWorkspaceEdit";
5-
import { wrapCommand, showChoicePalette } from "../novaUtils";
6+
import { wrapCommand } from "../novaUtils";
67
import { rangeToLspRange } from "../lspNovaConversions";
78

89
// @Panic: this is totally decoupled from typescript, so it could totally be native to Nova
@@ -43,7 +44,7 @@ export function registerCodeAction(client: LanguageClient) {
4344
return;
4445
}
4546

46-
const choice = await showChoicePalette(response, (c) => c.title, {
47+
const choice = await asyncNova.showChoicePalette(response, (c) => c.title, {
4748
placeholder: "Choose an action",
4849
});
4950
if (choice == null) {
@@ -77,7 +78,6 @@ export async function executeCommand(
7778
client: LanguageClient,
7879
command: lspTypes.Command
7980
) {
80-
console.info("executing command", command.command);
8181
const params: lspTypes.ExecuteCommandParams = {
8282
command: command.command,
8383
arguments: command.arguments,

src/main.test.ts

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ jest.mock("./tsLibPath", () => ({
77
jest.mock("./isEnabledForJavascript", () => ({
88
isEnabledForJavascript: () => true,
99
}));
10+
jest.mock("nova-extension-utils");
1011

1112
jest.useFakeTimers();
1213

@@ -59,6 +60,12 @@ describe("test suite", () => {
5960
const { activate, deactivate } = require("./main");
6061

6162
function resetMocks() {
63+
const {
64+
dependencyManagement: { installWrappedDependencies },
65+
} = require("nova-extension-utils");
66+
installWrappedDependencies
67+
.mockReset()
68+
.mockImplementation(() => Promise.resolve());
6269
nova.fs.access = jest.fn().mockReturnValue(true);
6370
(nova.commands.register as jest.Mock).mockReset();
6471
LanguageClientMock.mockReset().mockImplementation(() => ({
@@ -95,6 +102,14 @@ describe("test suite", () => {
95102
);
96103

97104
expect(CompositeDisposable).toBeCalledTimes(1);
105+
106+
const {
107+
registerDependencyUnlockCommand,
108+
} = require("nova-extension-utils").dependencyManagement;
109+
expect(registerDependencyUnlockCommand).toBeCalledTimes(1);
110+
expect(registerDependencyUnlockCommand).toBeCalledWith(
111+
"apexskier.typescript.forceClearLock"
112+
);
98113
});
99114

100115
function assertActivationBehavior() {
@@ -124,22 +139,20 @@ describe("test suite", () => {
124139
expect.any(Function)
125140
);
126141

127-
expect(Process).toBeCalledTimes(3);
128142
// installs dependencies
129-
expect(Process).toHaveBeenNthCalledWith(1, "/usr/bin/env", {
130-
args: ["npm", "install"],
131-
cwd: "/extension",
132-
stdio: ["ignore", "pipe", "pipe"],
133-
env: {
134-
NO_UPDATE_NOTIFIER: "true",
135-
},
136-
});
143+
144+
const {
145+
dependencyManagement: { installWrappedDependencies },
146+
} = require("nova-extension-utils");
147+
expect(installWrappedDependencies).toBeCalledTimes(1);
148+
149+
expect(Process).toBeCalledTimes(2);
137150
// makes the run script executable
138-
expect(Process).toHaveBeenNthCalledWith(2, "/usr/bin/env", {
151+
expect(Process).toHaveBeenNthCalledWith(1, "/usr/bin/env", {
139152
args: ["chmod", "u+x", "/extension/run.sh"],
140153
});
141154
// gets the typescript version
142-
expect(Process).toHaveBeenNthCalledWith(3, "/usr/bin/env", {
155+
expect(Process).toHaveBeenNthCalledWith(2, "/usr/bin/env", {
143156
args: ["node", "/tsLibPath/tsc.js", "--version"],
144157
stdio: ["ignore", "pipe", "ignore"],
145158
});
@@ -168,15 +181,6 @@ describe("test suite", () => {
168181
resetMocks();
169182

170183
(ProcessMock as jest.Mock<Partial<Process>>)
171-
.mockImplementationOnce(() => ({
172-
onStdout: jest.fn(),
173-
onStderr: jest.fn(),
174-
onDidExit: jest.fn((cb) => {
175-
cb(0);
176-
return { dispose: jest.fn() };
177-
}),
178-
start: jest.fn(),
179-
}))
180184
.mockImplementationOnce(() => ({
181185
onStdout: jest.fn(),
182186
onStderr: jest.fn(),
@@ -205,7 +209,7 @@ describe("test suite", () => {
205209
informationViewModule.InformationView
206210
>).mock.instances[0];
207211
expect(informationView.tsVersion).toBeUndefined();
208-
const tsVersionProcess: Process = ProcessMock.mock.results[2].value;
212+
const tsVersionProcess: Process = ProcessMock.mock.results[1].value;
209213
const exitCB = (tsVersionProcess.onDidExit as jest.Mock).mock.calls[0][0];
210214
exitCB(0);
211215
// allow promise to execute
@@ -228,19 +232,11 @@ describe("test suite", () => {
228232
global.console.warn = jest.fn();
229233
nova.workspace.showErrorMessage = jest.fn();
230234

231-
(ProcessMock as jest.Mock<Partial<Process>>).mockImplementationOnce(
232-
() => ({
233-
onStdout: jest.fn(),
234-
onStderr: jest.fn((cb) => {
235-
cb("some output on stderr");
236-
return { dispose: jest.fn() };
237-
}),
238-
onDidExit: jest.fn((cb) => {
239-
cb(1);
240-
return { dispose: jest.fn() };
241-
}),
242-
start: jest.fn(),
243-
})
235+
const {
236+
dependencyManagement: { installWrappedDependencies },
237+
} = require("nova-extension-utils");
238+
installWrappedDependencies.mockImplementation(() =>
239+
Promise.reject(new Error("Failed to install:\n\nsome output on stderr"))
244240
);
245241

246242
await activate();

src/main.ts

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { dependencyManagement } from "nova-extension-utils";
12
import { registerAutoSuggest } from "./commands/autoSuggest";
23
import { registerCodeAction } from "./commands/codeAction";
34
import { registerFindReferences } from "./commands/findReferences";
@@ -14,44 +15,19 @@ import { isEnabledForJavascript } from "./isEnabledForJavascript";
1415
nova.commands.register(
1516
"apexskier.typescript.openWorkspaceConfig",
1617
wrapCommand(function openWorkspaceConfig(workspace: Workspace) {
17-
workspace.openConfig("apexskier.typescript");
18+
workspace.openConfig();
1819
})
1920
);
2021

2122
nova.commands.register("apexskier.typescript.reload", reload);
2223

24+
dependencyManagement.registerDependencyUnlockCommand(
25+
"apexskier.typescript.forceClearLock"
26+
);
27+
2328
let client: LanguageClient | null = null;
2429
const compositeDisposable = new CompositeDisposable();
2530

26-
async function installWrappedDependencies() {
27-
return new Promise((resolve, reject) => {
28-
const process = new Process("/usr/bin/env", {
29-
args: ["npm", "install"],
30-
cwd: nova.extension.path,
31-
stdio: ["ignore", "pipe", "pipe"],
32-
env: {
33-
NO_UPDATE_NOTIFIER: "true",
34-
},
35-
});
36-
let errOutput = "";
37-
if (nova.inDevMode()) {
38-
process.onStdout((o) => console.log("installing:", o.trimRight()));
39-
}
40-
process.onStderr((e) => {
41-
console.warn("installing:", e.trimRight());
42-
errOutput += e;
43-
});
44-
process.onDidExit((status) => {
45-
if (status === 0) {
46-
resolve();
47-
} else {
48-
reject(new Error(`Failed to install:\n\n${errOutput}`));
49-
}
50-
});
51-
process.start();
52-
});
53-
}
54-
5531
async function makeFileExecutable(file: string) {
5632
return new Promise((resolve, reject) => {
5733
const process = new Process("/usr/bin/env", {
@@ -102,7 +78,7 @@ async function asyncActivate() {
10278
informationView.status = "Activating...";
10379

10480
try {
105-
await installWrappedDependencies();
81+
await dependencyManagement.installWrappedDependencies(compositeDisposable);
10682
} catch (err) {
10783
informationView.status = "Failed to install";
10884
throw err;
@@ -132,8 +108,8 @@ async function asyncActivate() {
132108
});
133109
console.log("logging to", logDir);
134110
// passing inLog breaks some requests for an unknown reason
135-
// const inLog = nova.path.join(logDir, "languageClient-in.log");
136-
const outLog = nova.path.join(logDir, "languageClient-out.log");
111+
// const inLog = nova.path.join(logDir, "languageServer-in.log");
112+
const outLog = nova.path.join(logDir, "languageServer-out.log");
137113
serviceArgs = {
138114
path: "/usr/bin/env",
139115
// args: ["bash", "-c", `tee "${inLog}" | "${runFile}" | tee "${outLog}"`],
@@ -158,6 +134,7 @@ async function asyncActivate() {
158134
env: {
159135
TSLIB_PATH: tslibPath,
160136
WORKSPACE_DIR: nova.workspace.path ?? "",
137+
INSTALL_DIR: dependencyManagement.getDependencyDirectory(),
161138
},
162139
},
163140
{

src/novaUtils.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,3 @@ export async function openFile(uri: string) {
1919
// try one more time, this doesn't resolve if the file isn't already open. Need to file a bug
2020
return await nova.workspace.openFile(uri);
2121
}
22-
23-
export async function showChoicePalette<T>(
24-
choices: T[],
25-
choiceToString: (choice: T) => string,
26-
options?: { placeholder?: string }
27-
) {
28-
const index = await new Promise<number | null>((resolve) =>
29-
nova.workspace.showChoicePalette(
30-
choices.map(choiceToString),
31-
options,
32-
(_, index) => {
33-
resolve(index);
34-
}
35-
)
36-
);
37-
if (index == null) {
38-
return null;
39-
}
40-
return choices[index];
41-
}

typescript.novaextension/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## future
4+
5+
### Changed
6+
7+
- Major behind the scene changes to dependency management
8+
- Performance improvements
9+
- More reliability with multiple open workspaces
10+
11+
### Fixed
12+
13+
- Use full completion result in manual auto-suggest command
14+
315
## v1.7.0
416

517
### Added

typescript.novaextension/extension.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@
108108
{
109109
"title": "Find Symbol",
110110
"command": "apexskier.typescript.findSymbol"
111+
},
112+
{
113+
"title": "Force Unlock Dependency Installation",
114+
"command": "apexskier.typescript.forceClearLock"
111115
}
112116
],
113117
"editor": [

typescript.novaextension/run.sh

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
11
#!/usr/bin/env bash
22

3-
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
4-
5-
# env > /dev/stderr
6-
# echo $PATH > /dev/stderr
7-
# dirname $(command -v node) > /dev/stderr
8-
# note: any output from this script will be used by nova's language server thing, so it'll break functionality
3+
# note: any output from this script will be used by nova's language server, so it'll break functionality
94

105
cd "$WORKSPACE_DIR"
116

127
# note: --tsserver-path=".../tsserver.js" doesn't support debugging since it tries to fork and bind two processes to the same port
138

14-
# path is stripped in the extension execution environment somehow
159
# symlinks have issues when the extension is submitted to the library, so we don't use node_modules/.bin
1610
node \
17-
"$DIR/node_modules/typescript-language-server/lib/cli.js" \
11+
"$INSTALL_DIR/node_modules/typescript-language-server/lib/cli.js" \
1812
--stdio \
1913
--tsserver-path="$TSLIB_PATH/tsserver.js"
2014

2115
# use this for debugging
2216
# node \
2317
# --inspect-brk \
24-
# "$DIR/../node_modules/typescript-language-server/lib/cli.js" \
18+
# "$INSTALL_DIR/node_modules/typescript-language-server/lib/cli.js" \
2519
# --stdio \
2620
# --tsserver-path="$TSLIB_PATH/tsserver"

0 commit comments

Comments
 (0)