Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
Add lsp command
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian McKenzie committed Mar 27, 2020
1 parent c7a5c82 commit 923b7dd
Show file tree
Hide file tree
Showing 16 changed files with 655 additions and 94 deletions.
9 changes: 9 additions & 0 deletions packages/@romejs/cli-flags/Parser.ts
Expand Up @@ -155,6 +155,15 @@ export default class Parser<T> {
continue;
}

// Allow for arguments to be passed as --foo=bar
const equalsIndex = name.indexOf('=');
if (equalsIndex !== -1) {
const cleanName = name.slice(0, equalsIndex);
const value = name.slice(equalsIndex + 1);
this.flags.set(cleanName, value);
continue;
}

// If the next argument is a flag or we're at the end of the args then just set it to `true`
if (rawArgs.length === 0 || this.looksLikeFlag(rawArgs[0])) {
this.flags.set(name, true);
Expand Down
20 changes: 18 additions & 2 deletions packages/@romejs/core/client/Client.ts
Expand Up @@ -49,7 +49,7 @@ type ClientOptions = {
stdout?: stream.Writable;
stderr?: stream.Writable;
stdin?: NodeJS.ReadStream;
flags: Partial<Omit<ClientFlags, 'clientName'>> & {clientName: string};
flags: Partial<Omit<ClientFlags, 'clientName'>>;
};

export type ClientProfileOptions = {
Expand Down Expand Up @@ -119,6 +119,14 @@ export default class Client {
requestResponseEvent: Event<ClientRequestResponseResult, void>;
endEvent: Event<void, void>;

setClientName(name: string) {
if (this.bridgeStatus !== undefined) {
throw new Error('Already connected to bridge. Cannot change client name');
}

this.flags.clientName = name;
}

getBridgeJSONFlags(): MasterBridgeJSONFlags {
return {
...this.flags,
Expand Down Expand Up @@ -523,10 +531,18 @@ export default class Client {
proc.kill();
}

console.log('ughhh???');
return undefined;
}

async connectToDaemon(): Promise<undefined | MasterBridge> {
const bridge = await this.tryConnectToExistingDaemon();
if (bridge !== undefined) {
return bridge;
}

return await this.startDaemon();
}

async tryConnectToNewDaemon(): Promise<undefined | MasterBridge> {
const bridge = await this.tryConnectToExistingDaemon();
if (bridge !== undefined) {
Expand Down
34 changes: 34 additions & 0 deletions packages/@romejs/core/client/commands.ts
Expand Up @@ -253,3 +253,37 @@ localCommands.set('status', {
}
},
});

localCommands.set('lsp', {
description: 'connect to an lsp',
category: commandCategories.PROJECT_MANAGEMENT,
defineFlags(consumer) {
// vscode-languageclient adds these on
consumer.get('stdio').asBooleanOrVoid();
consumer.get('clientProcessId').asStringOrVoid();
return {};
},

async callback(req: ClientRequest) {
// Better API
req.client.setClientName('lsp');
req.client.flags.silent = true;

const stdin = req.client.reporter.getStdin();
req.client.reporter.teardown();

const bridge = await req.client.findOrStartMaster();

bridge.lspBuffer.subscribe((chunk) => {
req.client.derivedReporterStreams.stdout.write(chunk);
});

stdin.on('data', (chunk) => {
bridge.lspBuffer.call(chunk.toString());
});

await new Promise(() => {});

return true;
},
});
5 changes: 5 additions & 0 deletions packages/@romejs/core/common/bridges/MasterBridge.ts
Expand Up @@ -154,4 +154,9 @@ export default class MasterBridge extends Bridge {
name: 'profile.stopWorker',
direction: 'server<-client',
});

lspBuffer = this.createEvent<string, void>({
name: 'lspBuffer',
direction: 'server<->client',
});
}
9 changes: 6 additions & 3 deletions packages/@romejs/core/common/fileHandlers.ts
Expand Up @@ -138,7 +138,8 @@ const textHandler: ExtensionHandler = {
},

async toJavaScript({file}) {
const src = await readFileText(file.real);
const src = file.sourceText === undefined
? await readFileText(file.real) : file.sourceText;
const serial = JSON.stringify(src);
return {
sourceText: `export default ${serial};`,
Expand Down Expand Up @@ -217,7 +218,8 @@ const jsonHandler: ExtensionHandler = {
},

async toJavaScript({file}) {
const src = await readFileText(file.real);
const src = file.sourceText === undefined
? await readFileText(file.real) : file.sourceText;

// Parse the JSON to make sure it's valid
const obj = parseJSON({
Expand Down Expand Up @@ -299,7 +301,8 @@ function buildJSHandler(

async toJavaScript({file}) {
return {
sourceText: await readFileText(file.real),
sourceText: file.sourceText === undefined
? await readFileText(file.real) : file.sourceText,
generated: false,
};
},
Expand Down
1 change: 1 addition & 0 deletions packages/@romejs/core/common/types/files.ts
Expand Up @@ -13,6 +13,7 @@ export type FileReference = {
uid: string;
real: AbsoluteFilePath;
remote: boolean;
sourceText?: string;
};

export type JSONFileReference = Omit<FileReference, 'real'> & {real: string};
Expand Down
18 changes: 14 additions & 4 deletions packages/@romejs/core/master/Master.ts
Expand Up @@ -59,6 +59,7 @@ import {
createUnknownFilePath,
} from '@romejs/path';
import {Dict} from '@romejs/typescript-helpers';
import {LSPConnection} from './lsp/index';

const STDOUT_MAX_CHUNK_LENGTH = 100_000;

Expand Down Expand Up @@ -375,11 +376,19 @@ export default class Master {
return;
}

await this.clientStartEvent.callOptional(client);
const connection = new LSPConnection(this, client, (chunk) => {
bridge.lspBuffer.send(chunk);
});

bridge.lspBuffer.subscribe((chunk) => {
connection.append(chunk);
});

bridge.query.subscribe(async (request) => {
return await this.handleRequest(client, request);
});

await this.clientStartEvent.callOptional(client);
}

async createClient(bridge: MasterBridge): Promise<MasterClient> {
Expand Down Expand Up @@ -474,8 +483,10 @@ export default class Master {
});

// Add reporter to connected set, important logs may be output to these
this.connectedReporters.addStream(errStream);
this.connectedReporters.addStream(outStream);
if (!flags.silent) {
this.connectedReporters.addStream(errStream);
this.connectedReporters.addStream(outStream);
}

const client: MasterClient = {
id: this.clientIdCounter++,
Expand Down Expand Up @@ -584,7 +595,6 @@ export default class Master {
});
}

// Create master request wrapper which is just a bucket of objects
const req = new MasterRequest({
client,
query,
Expand Down
2 changes: 1 addition & 1 deletion packages/@romejs/core/master/MasterRequest.ts
Expand Up @@ -430,7 +430,7 @@ export default class MasterRequest {
}

// Hint if all files were ignored
if (configCategory !== undefined) {
if (configCategory !== undefined && !ignoreProjectIgnore) {
const {paths: withoutIgnore} = await this.getFilesFromArgs({
...opts,
ignoreProjectIgnore: true,
Expand Down
91 changes: 15 additions & 76 deletions packages/@romejs/core/master/commands/format.ts
Expand Up @@ -8,90 +8,29 @@
import {MasterRequest} from '@romejs/core';
import {createMasterCommand} from '../../commands';
import {commandCategories} from '../../commands';
import {DiagnosticsProcessor, descriptions} from '@romejs/diagnostics';
import {FORMATTABLE_EXTENSIONS} from '@romejs/core/common/fileHandlers';
import {Consumer} from '@romejs/consume';

type Flags = {write: boolean};
import {createUnknownFilePath} from '@romejs/path';

export default createMasterCommand({
category: commandCategories.INTERNAL,
description: 'TODO',

defineFlags(consumer: Consumer): Flags {
return {
write: consumer.get('write').asBoolean(false),
};
},

async default(req: MasterRequest, flags: Flags): Promise<void> {
const {reporter} = req;

const {paths} = await req.getFilesFromArgs({
noun: 'formatting',
verb: 'linting',
configCategory: 'format',
extensions: FORMATTABLE_EXTENSIONS,
disabledDiagnosticCategory: 'format/disabled',
});

if (paths.size === 0) {
reporter.warn('No files formatted');
return;
}

const pathsByWorker = await req.master.fileAllocator.groupPathsByWorker(
paths,
);

const progress = reporter.progress();
progress.setTotal(paths.size);
progress.setTitle('Formatting');

const diagnosticsProcessor = new DiagnosticsProcessor({});

// TODO probably add the same logic in CompilerLinter if the project config disables formatting
await Promise.all(pathsByWorker.map(async (paths) => {
for (const path of paths) {
progress.setText(`<filelink target="${path.join()}" />`);
progress.tick();

const res = await req.requestWorkerFormat(path);
if (res === undefined) {
continue;
}

if (res.diagnostics.length > 0) {
diagnosticsProcessor.addDiagnostics(res.diagnostics);
continue;
}

if (!flags.write && res.formatted !== res.original) {
diagnosticsProcessor.addDiagnostic({
description: descriptions.LINT.PENDING_FIXES(
res.original,
res.formatted,
),
location: {
filename: path.join(),
},
});
} else {
//await writeFile(path, res.formatted);
}
}
}));
async default(req: MasterRequest): Promise<undefined | string> {
const {reporter, master} = req;
const {args} = req.query;
req.expectArgumentLength(1);

diagnosticsProcessor.maybeThrowDiagnosticsError();
const filename = await master.resolver.resolveEntryAssertPath({
...req.getResolverOptionsFromFlags(),
source: createUnknownFilePath(args[0]),
}, {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})});

if (flags.write) {
reporter.success(
`<number emphasis>${paths.size}</number> files formatted successfully`,
);
const res = await req.requestWorkerFormat(filename);
if (res === undefined) {
reporter.error('No formatter for this file');
return undefined;
} else {
reporter.success(
`<number emphasis>${paths.size}</number> files formatted correctly`,
);
reporter.writeAll(res.formatted);
return res.formatted;
}
},
});

0 comments on commit 923b7dd

Please sign in to comment.