Skip to content

Tweak project root heuristics #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
out
node_modules
client/server
.vscode-test
.vscode-test
**/*.vsix
3 changes: 2 additions & 1 deletion server/src/RescriptEditorSupport.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fileURLToPath } from "url";
import { RequestMessage } from "vscode-languageserver";
import * as utils from "./utils";
import * as project from "./project";
import * as path from "path";
import { exec } from "child_process";
import fs from "fs";
Expand All @@ -15,7 +16,7 @@ export let binaryExists = fs.existsSync(binaryPath);

let findExecutable = (uri: string) => {
let filePath = fileURLToPath(uri);
let projectRootPath = utils.findProjectRootOfFile(filePath);
let projectRootPath = project.findBscPath(filePath);
if (projectRootPath == null || !binaryExists) {
return null;
} else {
Expand Down
19 changes: 19 additions & 0 deletions server/src/fs_helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as Path from "path";
import Fs from "fs";

export let findPathRec = (
currentPath: string,
targetPath: string
): null | string => {
let baseDir = Path.dirname(currentPath);
if (Fs.existsSync(Path.join(baseDir, targetPath))) {
return baseDir;
} else {
if (baseDir === currentPath) {
// reached top
return null;
} else {
return findPathRec(baseDir, targetPath);
}
}
};
29 changes: 29 additions & 0 deletions server/src/project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as LSP from "vscode-languageserver-protocol";
import * as FsHelpers from "./fs_helpers";
import * as Const from "./constants";
import * as Path from "path";

// TODO: races here?
// TODO: this doesn't handle file:/// scheme
export let rootByPath = (
source: LSP.DocumentUri
): null | LSP.DocumentUri => {
return FsHelpers.findPathRec(source, Const.bscPartialPath);
};

export let bscPath = (source: LSP.DocumentUri): LSP.DocumentUri => Path.join(source, Const.bscPartialPath);

export let findBscPath = (
source: LSP.DocumentUri
): null | LSP.DocumentUri => {
let rootPath = rootByPath(source);
return rootPath == null ? null : bscPath(rootPath);
}

// the "build root" represents the nearest directory containing a "bsconfig.json" file.
// "bsconfig.json" can be used to locate the nearest build artifacts
export let findBuildRoot = (
source: LSP.DocumentUri
): null | LSP.DocumentUri => {
return FsHelpers.findPathRec(source, Const.bsconfigPartialPath);
};
36 changes: 18 additions & 18 deletions server/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import process from "process";
import * as p from "vscode-languageserver-protocol";
import * as Project from "./project";
import * as m from "vscode-jsonrpc/lib/messages";
import * as v from "vscode-languageserver";
import * as path from "path";
Expand Down Expand Up @@ -122,33 +123,33 @@ let openedFile = (fileUri: string, fileContent: string) => {

stupidFileContentCache.set(filePath, fileContent);

let projectRootPath = utils.findProjectRootOfFile(filePath);
if (projectRootPath != null) {
if (!projectsFiles.has(projectRootPath)) {
projectsFiles.set(projectRootPath, {
let buildRootPath = Project.findBuildRoot(filePath);
if (buildRootPath != null) {
if (!projectsFiles.has(buildRootPath)) {
projectsFiles.set(buildRootPath, {
openFiles: new Set(),
filesWithDiagnostics: new Set(),
bsbWatcherByEditor: null,
});
compilerLogsWatcher.add(
path.join(projectRootPath, c.compilerLogPartialPath)
path.join(buildRootPath, c.compilerLogPartialPath)
);
}
let root = projectsFiles.get(projectRootPath)!;
let root = projectsFiles.get(buildRootPath)!;
root.openFiles.add(filePath);
let firstOpenFileOfProject = root.openFiles.size === 1;
// check if .bsb.lock is still there. If not, start a bsb -w ourselves
// because otherwise the diagnostics info we'll display might be stale
let bsbLockPath = path.join(projectRootPath, c.bsbLock);
let bsbLockPath = path.join(buildRootPath, c.bsbLock);
if (firstOpenFileOfProject && !fs.existsSync(bsbLockPath)) {
let bsbPath = path.join(projectRootPath, c.bsbPartialPath);
let bsbPath = Project.bscPath(buildRootPath);
// TODO: sometime stale .bsb.lock dangling. bsb -w knows .bsb.lock is
// stale. Use that logic
// TODO: close watcher when lang-server shuts down
if (fs.existsSync(bsbPath)) {
let payload: clientSentBuildAction = {
title: c.startBuildAction,
projectRootPath: projectRootPath,
projectRootPath: buildRootPath,
};
let params = {
type: p.MessageType.Info,
Expand Down Expand Up @@ -179,17 +180,17 @@ let closedFile = (fileUri: string) => {

stupidFileContentCache.delete(filePath);

let projectRootPath = utils.findProjectRootOfFile(filePath);
if (projectRootPath != null) {
let root = projectsFiles.get(projectRootPath);
let buildRootPath = Project.findBuildRoot(filePath);
if (buildRootPath != null) {
let root = projectsFiles.get(buildRootPath);
if (root != null) {
root.openFiles.delete(filePath);
// clear diagnostics too if no open files open in said project
if (root.openFiles.size === 0) {
compilerLogsWatcher.unwatch(
path.join(projectRootPath, c.compilerLogPartialPath)
path.join(buildRootPath, c.compilerLogPartialPath)
);
deleteProjectDiagnostics(projectRootPath);
deleteProjectDiagnostics(buildRootPath);
if (root.bsbWatcherByEditor !== null) {
root.bsbWatcherByEditor.kill();
root.bsbWatcherByEditor = null;
Expand Down Expand Up @@ -411,11 +412,11 @@ process.on("message", (msg: m.Message) => {
process.send!(fakeSuccessResponse);
process.send!(response);
} else {
let projectRootPath = utils.findProjectRootOfFile(filePath);
if (projectRootPath == null) {
let bscPath = Project.findBscPath(filePath);
if (bscPath == null) {
let params: p.ShowMessageParams = {
type: p.MessageType.Error,
message: `Cannot find a nearby ${c.bsconfigPartialPath}. It's needed for determining the project's root.`,
message: `Cannot find a nearby ${c.bscPartialPath}. It's needed for determining the project's root.`,
};
let response: m.NotificationMessage = {
jsonrpc: c.jsonrpcVersion,
Expand All @@ -425,7 +426,6 @@ process.on("message", (msg: m.Message) => {
process.send!(fakeSuccessResponse);
process.send!(response);
} else {
let bscPath = path.join(projectRootPath, c.bscPartialPath);
if (!fs.existsSync(bscPath)) {
let params: p.ShowMessageParams = {
type: p.MessageType.Error,
Expand Down
20 changes: 1 addition & 19 deletions server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { Range } from "vscode-languageserver-textdocument";
import * as c from "./constants";
import * as childProcess from "child_process";
import * as p from "vscode-languageserver-protocol";
import * as path from "path";
import * as t from "vscode-languageserver-types";
import fs from "fs";
import * as os from "os";
import path from "path";

let tempFilePrefix = "rescript_format_file_" + process.pid + "_";
let tempFileId = 0;
Expand All @@ -16,24 +16,6 @@ export let createFileInTempDir = (extension = "") => {
return path.join(os.tmpdir(), tempFileName);
};

// TODO: races here?
// TODO: this doesn't handle file:/// scheme
export let findProjectRootOfFile = (
source: p.DocumentUri
): null | p.DocumentUri => {
let dir = path.dirname(source);
if (fs.existsSync(path.join(dir, c.bsconfigPartialPath))) {
return dir;
} else {
if (dir === source) {
// reached top
return null;
} else {
return findProjectRootOfFile(dir);
}
}
};

type execResult =
| {
kind: "success";
Expand Down
Loading