Skip to content
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

add task extension #690

Merged
merged 1 commit into from
Dec 18, 2017
Merged

add task extension #690

merged 1 commit into from
Dec 18, 2017

Conversation

marcdumais-work
Copy link
Contributor

Fixes #686

Signed-off-by: Marc Dumais [email protected]

Copy link

@hexa00 hexa00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very nice in general!!

I did a first pass but I think we need to design this a bit more.

I'll do a second pass later.


const pty = require("node-pty");

export const TerminalProcessOptions = Symbol("TerminalProcessOptions");
export interface TerminalProcessOptions {
command: string,
args?: string[],
options?: object
options?: child.SpawnOptions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct as these are the node-pty options rather than the child_process options.

this.taskWatcher.onTaskOutputEntryFound((event: ITaskOutputEntryFoundEvent) => {
if (ProblemMarker.is(event.entry)) {
// problem marker
this.messageService.info(`Task ${event.taskId} has sent a problem marker`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService
) {
this.taskWatcher.onTaskOutputEntryFound((event: ITaskOutputEntryFoundEvent) => {
if (ProblemMarker.is(event.entry)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we iterate on the marker times and choose a proper handler?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed this, since we do not yet generate markers in the backend. Will be back as part of the patch that will add parsing/handling of tasks output.

let tasks: string[];

tasks = await this.taskServer.getTasks();
return tasks;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just return await ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

protected attachTerminal(terminalId: number, taskId: number): void {
const newTerminal = this.terminalWidgetFactory({
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use the WidgetManager here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to avoid having the task terminal(s) be managed by the widget manager. e.g. I do not want those terminals to be saved/restored by the frontend (make no sense, I think for tasks).

Copy link

@hexa00 hexa00 Oct 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should expand the widget manager to allow these features and still use it otherwise we're duplicating code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now using widget manager to create the terminal widget for tasks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened issue #776, asking about widget restoration be optional

} else {
// look for command relative to cwd
const resolved_command = FileUri.fsPath(new URI(cwd).resolve(command));
if (fs.existsSync(resolved_command)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto sync

// Check if task command can be found, if so return its resolved
// absolute path, else returns undefined
private findCommand(command: string, cwd: string): string | undefined {
const systemPath = process.env.PATH;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure this is windows friendly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends what you mean. I think it does work to get the system path, to try to find an executable in it. However, in many cases on windows, the command, that here we confirm exists, will just be "cmd.exe" and the real execution target will be passed as a parameter. ex: cmd.exe /c test.bat . In that sense, this is not so windows-friendly.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just mean I'm not sure PATH is the env var windows uses.

So you mean if need to test cmd.exe and the destination ? I think we can assume cmd.exe exists no ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can assume that cmd.exe exists. The user could in theory point towards an alternate interpreter. The destination can be a script/executable but can also be an interpreter built-in, like "DIR". So we may not find it as a file in that case, even if we look.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed that's just the situation I wanted to avoid.

What if we were to try to run it, and after that if it fails try to narrow down the reason using findCommand?

That way these cases would be avoided most likely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting idea; I like it. This way we at least try to execute the command, even if we can't figure-out exactly what it's calling. If it succeeds, everything is good. If it fails, we dig down to try and see if we can figure-out why it failed, looking into locating the command, maybe in some cases its file parameter(s).

I do not think we avoid the corner cases entirely, however. There will still be cases like : "cmd.exe /c DIZ" that will be hard to pin down: did the user want the "DIR" built-in of cmd.exe and made a typo? Or was he trying to execute a DIZ.bat batch file in cwd?


const separator = isWindows ? ";" : ":";

// just a command without path?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would just be simpler to try it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. The reason for findCommand() is try to figure-out if the command/script, we're about to try executing, can be found. We could just skip ahead and try to execute it; if it's then found and executed successfully, then no problem. But if it's not found or not successful, it can be difficult to distinguish what happened:

  • the command was found and run, but exited with an error status (rc !=0)
  • the command was not found (rc != 0)

So, in findCommand() we try to determine if the command/script seems to exist, so that we can more correctly assume that any error is a result of its execution, not of it not having been found.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I wish RawProcess/TerminalProcess would take care of that, I know I can know it for RawProcess but I don't think it's possible now for the Terminal.

So OK for now and we'll try to improve that upstream

export class Task {
protected taskId: number;
protected taskProcess: Process;
// linter: string | undefined;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

this.taskId = this.taskManager.register(this);
this.taskProcess = process;

this.taskProcess.onExit(event => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also need to add ProcessManager.delete(this.taskProcess)
And cleanup the onExit listener.

Maybe this could be doner better in general we need to think about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done both

@svenefftinge
Copy link
Contributor

Could we reuse the format from vs code? (https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson)
Those files are checked into repositories and there are already quite a good number of those in e.g. github repos: https://github.com/search?utf8=%E2%9C%93&q=problemMatcher+extension%3Ajson&type=Code

It would make it easier for people to switch between vs code and cloud based theia workspaces.

@marcdumais-work
Copy link
Contributor Author

@svenefftinge Possibly. We do not have, ATM as rich a feature-set as vs code does, w-r to tasks, matchers, etc. So just re-using their format "as-is" might give a false sense of hope that the features will work, and work the same way. But we could try to be more similar, when it makes sense. e.g. ErrorMatcher vs ProblemMatcher.

WDYT?

@svenefftinge
Copy link
Contributor

As close as possible, doesn't really help with the reuse. But can't the problemMatcher stuff be considered a nice-to-have optional feature anyways? So I would say we could log out a warning about features that are not yet supported, as long as we can start the process.

@svenefftinge
Copy link
Contributor

The current design is based on the assumptions of a single workspace and a fixed storage for user settings. As discussed in other contexts, the TaskConfiguration should be in the frontend based on user-storage service (user level) and file system watch (workspace level).
The TaskServer would only have run and kill and always gets the TaskOptions as parameter to run.

@marcdumais-work
Copy link
Contributor Author

marcdumais-work commented Dec 1, 2017

In this new version I moved the handling of task configuration files to the frontend.

The backend server is now a singleton, and it supports multiple clients. All clients that connect are remembered and task-related events are broadcasted to all clients, and includes a context field that is given by the client, when it calls run() . Currently the client uses its current WS root URI string as context. It filters received events and only show them to the user when the event's context matches their own.

This way multiple clients, using a same WS simultaneously, can each show their respective users, e.g. that a specific task has just been started. The user(s) that did not start the task in their client will then have the option of "attaching" to the task, opening a terminal to see the task's output so far, going back to the beginning, if it stays within reasonable limits, and see new outputs as they happen.

I have not yet added the user-level configuration; that can be done in a separate issue.

TODO now:

  • Investigate/ fix the 3 disabled (skipped) unit tests (on Windows only).
  • replace the JSON parser used in the FE to parse tasks.json, to put back the one from VS Code, that has better diagnostics. Patrick has made it work recently in the FE, so I can do similar

TODO later:

  • enable the task commands only when approproate (e.g. when there are task configurations present in the WS) [open a new issue]

  • add "global" user-level task configurations, using user-storage [open a new issue]

  • add support for VS Code task configuration format, including ProblemMatchers [open a new issue]

  • Integrate output parser ([WIP] (process) output parser  #605) . Each task configuration can, optionally, have a matcher sub-configuration, that contains information for the parser to identify interesting outputs, and parse them. The resulting parsed matches can then be sent to the client(s). e.g. a C++ build task might contain a matcher that can identify GCC errors and warnings, from which problem markers can be created. [open a new issue]

  • add support for "onStart" tasks, that are executed automatically upon a workspace being "started". e.g. initial cloning of git repos. [open a new issue]

  • Assisted task configuration creation, from the Run Task command. Open tasks.json in editor and add a skeleton for a new task configuration and let the user complete it. [open a new issue]

  • use a JSON schema to validate tasks config file, have help in the editor for the user to fill-in configurations correctly. [open a new issue]

  • fix issue: task terminal widget restore works well when the task is still running. But if it's no longer, TerminalWidget.start() fails to attach to the task's process, and fallbacks to creating an interactive terminal. In that instance the layout manager calls widget.start(), not the task service, so it can't, e.g. check if a process still exists and if not skip calling start(). [open a new issue]

  • notifications about tasks being started / terminated: its a bit annoying to have to click on them to dismiss them. Would be cool to have auto-dismissed notifications, that dismiss after a delay. [open a new issue]

  • add status bar contribution for tasks. e.g. the "show running tasks" status bar button in VS Code. Calls 'attach to running task' task command, that opens the candidate running tasks, in the quick open menu. [open a new issue]

  • probably more...

* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

export * from './task-frontend-module';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be exported. is it needed anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed - removed.


import { inject, injectable } from "inversify";
import { TaskService } from './task-service';
import { ITaskInfo } from '../common/task-protocol';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The I prefix is now against the code guidelines... in favor of noprefix and Impl suffix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct - Renamed all I*

@@ -0,0 +1,17 @@
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not happen... unless there's 2 conflicting versions.
I deleted that file, redid a yarn and only got a modified yarn.lock
So this file should be removed from git.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct - the dependency in there is not longer used. File removed.

if (this.task.processId) {
// reset focus on the previously active element.
this.activeElement.focus();
// this.taskService.run(this.taskLabel);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left over ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed left-over line

}));
}

init(client: TaskConfigurationClient) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be setClient ? like the others

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed method

resolve(command);
return;
} else {
const uri = new URI(cwd).resolve(command);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I get this it's absolute, and it doesn't exist but we will check for cwd/absolutepath ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, this looks like dead code; if a command with an absolute path is not found, there's not much more we can do. Removed.

}
} else {
// look for command relative to cwd
const resolved_command = FileUri.fsPath(new URI(cwd).resolve(command));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About CWD should it default to workspaceRoot somewhere? I don't think it makes sense to use the process.cwd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the refactorings to support multiple clients, the Task backend server has become stateless, with regards to the workspace being used (it used to be that each client would get it's own instance of the Task server, that would know what the WS Root was). The backend doesn't know which workspace(s) are being used.

Instead, each task configuration defines what "CWD" should be used by that task. The F-E will replace the symbolic value "$workspace" by its value, if found in the task configuration's cwd. In the end, that same CWD value is used to create the process, used by the task.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update: now cwd defaults to the WS Root, unless otherwise defined in the task configuration (set by frontend).

return;
} else {
// should cover Unix and Windows cases
const separator = /;|:/;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

: is valid as a linux file we should check for isWindows and select 1 separator only.

You can use: https://nodejs.org/api/path.html#path_path_delimiter

➜  ~ touch ab:cd
➜  ~ ls -la ./ab:cd 
-rw-r--r-- 1 eantotr eusers 0 Dec  4 14:22 ./ab:cd
➜  ~ touch abc;cd
➜  ~ ls -l ab*
-rw-r--r-- 1 eantotr eusers 0 Dec  4 14:23 abc
-rw-r--r-- 1 eantotr eusers 0 Dec  4 14:22 ab:cd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, I didn't know about path.delimiter. Since we get that value from node, I think it already takes into account which platform we are on.

Done (except handling the case where there is a ":" used within a directory in the system path - Sorry, if you do that, you deserve for your executable not to be found ;)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except handling the case where there is a ":" used within a directory in the system path - Sorry, if you do that, you deserve for your executable not to be found

That won't happen since I guess on windows you can't create a file or directory with : like you can't do a file /dir with ; in linux ?

echo tasking... %%x
@REM sleep for ~1s
@REM see: https://stackoverflow.com/questions/735285/how-to-wait-in-a-batch-script
ping 192.0.2.2 -n 1 -w 1000> nul
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird IP why not 127.0.0.1 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The answer is in the link above: basically this is garanteed, by definition, to be an address that can't be assigned. So we are certain that no one will answer and that we will spend the "1000" ms waiting. So basically a poor man's "sleep". If we used 127.0.0.1, it would answer immediately and not wait for 1000ms.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right OK!

@@ -0,0 +1,22 @@

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove leave in github

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

"@theia/process": "^0.2.3",
"@theia/terminal": "^0.2.3",
"@theia/workspace": "^0.2.3",
"require-strip-json-comments": "^1.0.2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought jsonc-parser ended up being used by modifying the webpack config file? If that is also used it's fine though.

Also if you really use jsonc-parser, shouldn't that dependency be specifically mentionned in the task package.json?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@epatpol I think you were looking at the previous version? This is no longer in there.

Copy link
Contributor Author

@marcdumais-work marcdumais-work Dec 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if you really use jsonc-parser, shouldn't that dependency be specifically mentionned in the task package.json?

It's added in the latest version

@@ -0,0 +1,73 @@
{
// Some sample Theia development tasks
"tasks": [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like having a sample tasks.json but maybe the sample config should be in the package README? Or the supplied sample tasks should be commented out? I'm not sure master should have default tasks when starting theia. Maybe having a package README which would describe a sample task and go in details about every attributes? Or simply a link to vscode's page on the task.json file attributes if they're very similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestions. I removed the example tasks.json and added a README, in which I list the example tasks.

// comment
"tasks": [
{
"label": "workspace: failing task",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these test tasks? Shouldn't they go into some test-resource forlder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they are example / manual test tasks. At Patrick's suggestion, I deleted that file and instead added the task configurations as examples, to the README.md for the extension.

protected taskService: TaskService
) {
super();
this.activeElement = window.document.activeElement as HTMLElement;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to make much difference, either way, in this case. If we do proceed to create or attach to a task, we may not want to restore the previous focus anyway, and focus on the new terminal. Removed.

return false;
}
// reset focus on the previously active element.
this.activeElement.focus();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

protected taskService: TaskService
) {
super();
this.activeElement = window.document.activeElement as HTMLElement;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed too

}
if (this.task.processId) {
// reset focus on the previously active element.
this.activeElement.focus();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dito

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed too

}

run(task: TaskOptions, ctx?: string): Promise<TaskInfo> {
return this.doRun(task, ctx);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why delegate to doRun?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No good reason any more -> removed run(), renamed doRun() to run().

}

protected doRun(options: TaskOptions, ctx?: string): Promise<TaskInfo> {
return new Promise<TaskInfo>(async (resolve, reject) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary outer promise, async/await?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored to use async/await

// because the executable or script was not found, or if it was found, ran, and exited
// unsuccessfully. So here we look to see if it seems we can find a file of that name that
// is likely to be the one we want, before attemting to execute it.
this.findCommand(command, cwd)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we do findCommand? Aren't the error message from the process factories good?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, not so much. It's difficult to distinguish between the cases where a script or binary is found and executed, with an abnormal exit, versus the script/binary not being found at all (I think because of limitations in node-pty). Maybe we should enhance node-pty?

context: ctx
});

const toDispose = new DisposableCollection();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like there is always only one disposable in the collection so we could remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

processId: (options.processType === 'raw') ? proc.id : undefined,
command: cmd,
label: options.label,
ctx: ctx === undefined ? '' : ctx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we extract the translation to a method and use it also from here https://github.com/theia-ide/theia/pull/690/files#diff-fccfd5a001011db19d656ecd3d66d03dR67

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea - done

});
}

protected hasWatchedFileChanged(changes: FileChange[]): boolean {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a marker for @akosyakov #942

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should not use the watcher server in the frontend, it is the impl details of FileSystemWatcher, the watcher takes care of servers and reconnection to them if a connection is down as well as preserving watching.

I am working on PR which will add an optional callback to FileSystemWatcher.watch(uri): Promise<Disposable> that you can pass it to avoid dispatching changes to other clients.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akosyakov ok, I will update this code accordingly, when your PR is ready.

Copy link

@hexa00 hexa00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just nits, OK with the changes

export class TaskConfigurations implements Disposable {

protected readonly toDispose = new DisposableCollection();

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

protected readonly toDispose = new DisposableCollection();

protected tasksMap = new Map<string, TaskOptions>();

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
}
} else {
this.logger.info(`Config file tasks.json does not exist under ${rootUri}`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to warn ?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be normal that the user doesn't have one...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, tasks.json presence is optional. done


if (await this.fileSystem.exists(cfgFile)) {
return cfgFile;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing else { return Promise.resolve(undefined) }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following previous changes, this is no longer applicable (method no longer async now that it only computes the potential task config file URI)

}
}

protected async getConfigFile(rootDir: string): Promise<string | undefined> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would separate the existance from getConfigFile like:

getConfigFileURI

then in code inline this.filesystem.exits(getConfigFileURI(rootDir))

but just a nit.. do as you want

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not - done.

* @param command command name to look-for
* @param cwd current working directory
*/
private async findCommand(command: string, cwd: string): Promise<string | undefined> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected ?

@injectable()
export class QuickOpenTask implements QuickOpenModel {

private items: QuickOpenItem[];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
}

private filterDuplicates(tasks: TaskOptions[]): TaskOptions[] {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

widget.start(terminalId);
}

private isEventForThisClient(context: string | undefined): boolean {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. BTW, I noticed in general that we tend not to explicitly mark methods as "public". I was doing it in this class, so I removed these.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK yes that's still a gray zone to me.

};
}

private onTaskCreatedEmitter = new Emitter<TaskInfo>();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@marcdumais-work
Copy link
Contributor Author

@hexa00 Thanks for the second review pass. Let's see if the unit tests still pass!

@marcdumais-work
Copy link
Contributor Author

Git issue during Windows tests again - will slightly modify PR and push again, to trigger new tests

@marcdumais-work
Copy link
Contributor Author

almost this time - electron test failure

@marcdumais-work
Copy link
Contributor Author

ok, the Windows tests finally went went through.

@svenefftinge I plan to rebase this on the latest master and merge probably on Monday morning, Montreal time. Please let me know if you have objections.

This extension provides the means to execute scripts or executables,
from a frontend client, in the backend. Tasks life cycle events are
send from the backend to the frontend clients, whenever a task is
created or terminates. A task client can ask the backend for the list
of tasks that are currently running.

We support having multiple frontend task clients that are using the same
backend task server. Each client uses its workspace root as context
parameter, in all its communications with the backend. Whenever the
backend sends notifications to its clients, the original client context
is sent along. It's then up-to the clients to determine if they are
interested in the event, given its context, and show it to the user or
not.

This mechanism makes it possible for multiple clients, opened on the
same workspace, to all get the notifications related to all tasks that
execute from that workspace. At the same time, clients can ignore tasks
that are run from another workspace.

Fixes #686

Signed-off-by: Marc Dumais <[email protected]>
) {
this.toDispose.push(watcherServer);

watcherServer.setClient({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcdumais it is bogus, there is only one FileSystemWatcherServer which is used in FileSystemWatcher. With this change, either FileSystemWatcher or TaskConfiguration won't receive changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants