this.onClickDiv()} />;
}
protected onClickDiv(): void {
// do stuff
}
}
// very bad
class MyWidget extends ReactWidget {
render(): React.ReactNode {
return
;
}
protected onClickDiv(): void {
// do stuff, no `this` access
}
}
// good
class MyWidget extends ReactWidget {
render(): React.ReactNode {
return
}
protected onClickDiv = () => {
// do stuff, can access `this`
}
}
```
## URI/Path
* [1.](#uri-over-path) Pass URIs between frontend and backend, never paths. URIs should be sent as strings in JSON-RPC services, e.g. `RemoteFileSystemServer` accepts strings, not URIs.
> Why? Frontend and backend can have different operating systems leading to incompatibilities between paths. URIs are normalized in order to be OS-agnostic.
* [2.](#frontend-fs-path) Use `FileService.fsPath` to get a path on the frontend from a URI.
* [3.](#backend-fs-path) Use `FileUri.fsPath` to get a path on the backend from a URI. Never use it on the frontend.
* [4.](#explicit-uri-scheme) Always define an explicit scheme for a URI.
> Why? A URI without scheme will fall back to `file` scheme for now; in the future it will lead to a runtime error.
* [5.](#frontend-path) Use `Path` Theia API to manipulate paths on the frontend. Don't use Node.js APIs like `path` module. Also see [the code organization guideline](code-organization.md).
* [6.](#backend-fs) On the backend, use Node.js APIS to manipulate the file system, like `fs` and `fs-extra` modules.
> Why? `FileService` is to expose file system capabilities to the frontend only. It's aligned with expectations and requirements on the frontend. Using it on the backend is not possible.
* [7.](#use-long-name) Use `LabelProvider.getLongName(uri)` to get a system-wide human-readable representation of a full path. Don't use `uri.toString()` or `uri.path.toString()`.
* [8.](#use-short-name) Use `LabelProvider.getName(uri)` to get a system-wide human-readable representation of a simple file name.
* [9.](#use-icon) Use `LabelProvider.getIcon(uri)` to get a system-wide file icon.
* [10.](#uri-no-string-manipulation) Don't use `string` to manipulate URIs and paths. Use `URI` and `Path` capabilities instead, like `join`, `resolve` and `relative`.
> Why? Because object representation can handle corner cases properly, like trailing separators.
```ts
// bad
uriString + '/' + pathString
// good
new URI(uriString).join(pathString)
// bad
pathString.substring(absolutePathString.length + 1)
// good
new Path(absolutePathString).relative(pathString)
```
## Logging
* [1.](#use-named-loggers) Whenever you need to log and you are within an Inversify context, inject a named logger
```ts
// bad
@inject(ILogger)
protected readonly logger: ILogger;
// without a named logger this is the same call as 'console.info'
this.logger.info(``);
// good
@inject(ILogger) @named('my-logger')
this.logger.info(``)
```
> Why? The log level of all named loggers can be separately configured and even changed at runtime. This is useful for filtering logs to only the use cases the developer is interested in. See [here](https://github.com/eclipse-theia/theia/tree/master/packages/core#logging-configuration) for more information.
* [2.](#naming-loggers) Use the following convention when naming loggers: `[optional-purpose]package-name:class-name#optional-suffix`
```ts
// bad
@inject(ILogger) @named('MyClass')
protected readonly logger: ILogger;
// good
@inject(ILogger) @named('remote:BackendRemoteServiceImpl');
```
> Following this convention allows to conveniently configure log levels for purpose, packages and concrete classes.
## Workspace Trust
Theia supports [Workspace Trust](https://theia-ide.org/docs/workspace_trust/) to protect users from potentially harmful code in untrusted repositories. Features that execute code or load external content from the workspace must be gated behind workspace trust.
* [1.](#workspace-trust-check) Features that execute workspace-provided code (tasks, debug configurations, scripts) or load workspace-provided content (prompt templates, configuration files that influence behavior) must check workspace trust before proceeding. Use `WorkspaceTrustService` to check or request trust.
```ts
@inject(WorkspaceTrustService)
protected readonly workspaceTrustService: WorkspaceTrustService;
// Check current trust state
if (!this.workspaceTrustService.isWorkspaceTrusted()) {
return; // disable the feature
}
// Or request trust from the user (shows dialog if not yet decided)
const trusted = await this.workspaceTrustService.requestWorkspaceTrust();
if (!trusted) {
return;
}
```
* [2.](#workspace-trust-restrictions) When a feature is restricted in untrusted workspaces, register a `WorkspaceRestrictionContribution` so that the Restricted Mode status bar tooltip can inform the user about what is disabled.
```ts
bind(WorkspaceRestrictionContribution).toDynamicValue((): WorkspaceRestrictionContribution => ({
getRestrictions(): WorkspaceRestriction[] {
return [{
label: nls.localize('theia/myPackage/restricted', 'My Feature is disabled in Restricted Mode')
}];
}
})).inSingletonScope();
```
* [3.](#workspace-trust-context-key) Use the `isWorkspaceTrusted` context key when workspace trust should control menu or command visibility.
## "To Do" Tags
There are situations where we can't properly implement some functionality at the time we merge a PR. In those cases, it is sometimes good practice to leave an indication that something needs to be fixed later in the code. This can be done by putting a "tag" string in a comment. This allows us to find the places we need to fix again later. Currently, we use two "standard" tags in Theia:
* `@stubbed`
This tag is used in VS Code API implementations. Sometimes we need an implementation of an API in order for VS Code extensions to start up correctly, but we can't provide a proper implementation of the underlying feature at this time. This might be because a certain feature has no corresponding UI in Theia or because we do not have the resources to provide a proper implementation.
Using the `@stubbed` tag in a JSDoc comment will mark the element as "stubbed" on the [API status page](https://eclipse-theia.github.io/vscode-theia-comparator/status.html)
* `@monaco-uplift`
Use this tag when some functionality can be added or needs to be fixed when we move to a newer version of the monaco editor. If you know which minimum version of Monaco we need, you can add that as a reminder.