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 Qt help view #40

Merged
merged 5 commits into from
Nov 28, 2020
Merged
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
5 changes: 5 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ jobs:
# npm test
env:
CI: true
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: qtvsctools.vsix
path: '*.vsix'
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## 0.8

- Add Qt online help commands. Thanks to for the request [@Shatur95](https://github.com/Shatur95)

## 0.7

- Add support for open multiple selected files at once in Qt designer. Thanks to [@cobalt77](https://github.com/cobalt77) for the contribution 🙏.
Expand Down
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ This is a Qt extension for VSCode. It is designed to be an similar tool to the [
At the moment the extension extracts the Qt file locations from CMake only (from [CMake Tools extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) setting `cmake.buildDirectory`), so choosing and different Qt version from disk is not supported at the moment (but if you use cmake already, everything is automatically detected :-) ).

## Features

* [x] Launch Qt Designer
* [x] Edit `.ui` file in Qt Designer
* [x] Launch Qt Assistant
* [x] Launch Visual Studio (Windows only)
* [x] Launch Qt online documenation
* [x] Launch Visual Studio (Windows only)
* [x] Launch Qt Creator<br>
`.ui` and `.qrc` files can be opened in Qt Creator. You can also open the whole workspace in Qt Creator too.<br>
This extension try to detect the Qt Creator installation automatically (on Windows and MacOS). You can set the executable path via `qttools.creator` settings if the extension can't find Qt Creator (for whatever reason)
Expand All @@ -26,25 +28,54 @@ At the moment the extension extracts the Qt file locations from CMake only (from
* ...

## Requirements

* You need to have the [CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) installed, because this extension extracts some data from it!
* [C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) is highly recommended

## Limitations

* There are some situation where the automatic detection mechanism of Qt is not working. If that is the case you can always trigger the `Scan for Qt kits` command in the command palette.
* The debugger extension use normal natvis xml files (used via the `launch.json` setting `visualizerFile` from the [C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) ). They work really well on windows, but on mac and linux there are some problems, because it is not based on the same implementation. If you have any problems with them create an issue on their issue tracker.

## Variable substitution

The `cmake.buildDirectory` from `cmake tools` support variable substitution which looks like `${myvariable}` (example `${generator}`).

This extension supports every variable substitution from `cmake tools` when the `cmake tools` extension is installed and active.

If `cmake tools` is not active the extension will fallback to the content of the `cmake.buildDirectory`. In this mode only `${buildType}`, `${buildKit}` and `${workspaceFolder}` are supported variable substitutions!

## Online help

The Qt online help can be used with this extension. Right now only the latest Qt 5 version will be searched.

You have 2 commands:

* Qt: Online help
This will open the Qt documentation. When you are in a `.cpp` or `.h` file and your cursor is inside a text block then the command will search that word as a class in the documenation.
* Qt: Search online help
This command will create a textbox inside vscode where you can enter your search term. This search term will be send to the search of the Qt Documenation.

By default the qt website will be opened inside VSCode itself.

The integrate webview has some limitations:

* Open the find menu via `CTRL` + `F` does not work in many scenarios, see [#96307](https://github.com/microsoft/vscode/issues/96307)
Most of the time it works when you can click on the document tab (Qt online help) and then press `CTRL` + `F`
* The normal mouse-click on a link which would open a new tab also don't work
You can click the middle mouse button on that link and it will open in your external browser.
* No navigation buttons

You can also turn of the embedded webview for the online help and use your external browser by setting the `qttools.useExternalBrowser` to `true`. Be aware that you will get a popup from VSCode which informs you about opening an external website. To avoid getting this popup every time just press on `Configure Trsuted Domains` and choose `trust qt.io and all its subdomains`.

## Troubleshooting

If you have problems with the extension just file a issue on [GitHub](https://github.com/tonka3000/vscode-qt-tools/issues). It's mostly a good idea to attach the log output of this extension to the issue. You can active the logger by adding `"qttools.loglevel": "debug"` to your `settings.json` file. Just copy the content of the `Qt` output pane into your GitHub issue.

## Contributions

Pull requests are welcome :-D

## License

MIT
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "qtvsctools",
"displayName": "Qt tools",
"description": "Qt tools support for VSCode",
"version": "0.7.0",
"version": "0.8.0",
"publisher": "tonka3000",
"preview": true,
"license": "MIT",
Expand All @@ -25,6 +25,8 @@
"onCommand:qttools.currentfileindesigner",
"onCommand:qttools.launchdesigneronly",
"onCommand:qttools.launchassistant",
"onCommand:qttools.launchonlinehelp",
"onCommand:qttools.searchonlinehelp",
"onCommand:qttools.launchcreatoronly",
"onCommand:qttools.workspaceincreator",
"onCommand:qttools.currentfileincreator",
Expand All @@ -49,6 +51,16 @@
"title": "Launch Qt Assistant",
"category": "Qt"
},
{
"command": "qttools.launchonlinehelp",
"title": "Online help",
"category": "Qt"
},
{
"command": "qttools.searchonlinehelp",
"title": "Search Online help",
"category": "Qt"
},
{
"command": "qttools.scanqtkit",
"title": "Scan for Qt Kit",
Expand Down Expand Up @@ -151,6 +163,12 @@
"description": "automatically inject the natvis.xml of Qt into existing launch.json entries",
"scope": "resource"
},
"qttools.useExternalBrowser": {
"type": "boolean",
"default": false,
"description": "use external browser for online help",
"scope": "resource"
},
"qttools.visualizerFile": {
"type": "string",
"default": "",
Expand Down
72 changes: 72 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { NatvisDownloader } from './downloader';
import * as open from 'open';
import { Logger, LogLevel } from './logging';
import { fileExists } from './tools';
import { QtHelp } from './help';

class ExtensionManager implements vscode.Disposable {
public qtManager: qt.Qt | null = null;
Expand All @@ -19,9 +20,11 @@ class ExtensionManager implements vscode.Disposable {
private _cmakeCacheWatcher: fs.FSWatcher | null = null;
public natvisDownloader: NatvisDownloader | null = null;
public logger: Logger = new Logger();
public help: QtHelp | null = null;

constructor(public readonly extensionContext: vscode.ExtensionContext) {
this._context = extensionContext;
this.help = new QtHelp(extensionContext);
this._channel = vscode.window.createOutputChannel("Qt");
this.logger.outputchannel = this._channel;
this.qtManager = new qt.Qt(this._channel, this._context.extensionPath);
Expand All @@ -45,6 +48,9 @@ class ExtensionManager implements vscode.Disposable {
this._channel.appendLine("update state of ExtensionManager");
this.logger.level = this.getLogLevel();
this.logger.debug("update state");
if (this.help) {
this.help.useExternalBrowser = this.getUseExternalBrowserForOnlineHelp();
}
if (this.cmakeCache) {
this.logger.debug(`cmake build directory: ${await this.getCMakeBuildDirectory()}`);
this.cmakeCache.filename = await this.getCmakeCacheFilename();
Expand Down Expand Up @@ -92,6 +98,16 @@ class ExtensionManager implements vscode.Disposable {
return result;
}

public getUseExternalBrowserForOnlineHelp(): boolean {
let result = false;
const workbenchConfig = vscode.workspace.getConfiguration();
let useExternalBrowser = workbenchConfig.get('qttools.useExternalBrowser') as boolean;
if (useExternalBrowser) {
result = useExternalBrowser;
}
return result;
}

public getLogLevel(): LogLevel {
const config = vscode.workspace.getConfiguration();
const logleveltext = config.get("qttools.loglevel") as string;
Expand Down Expand Up @@ -366,6 +382,9 @@ class ExtensionManager implements vscode.Disposable {
if (this.logger) {
this.logger.dispose();
}
if (this.help) {
this.help.dispose();
}
this.natvisDownloader = null;
}

Expand Down Expand Up @@ -477,6 +496,59 @@ export async function activate(context: vscode.ExtensionContext) {
}
});

_EXT_MANAGER.registerCommand('qttools.launchonlinehelp', async () => {
if (_EXT_MANAGER && _EXT_MANAGER.qtManager) {
await _EXT_MANAGER.updateState();
try {
let word = "";
const editor = vscode.window.activeTextEditor;
if (editor) {
if (editor.document.fileName.endsWith(".cpp") || editor.document.fileName.endsWith(".h")) {
if (editor.selection.isEmpty) {
const wordrange = editor.document.getWordRangeAtPosition(editor.selection.active);
if (wordrange) {
word = editor.document.getText(wordrange);
_EXT_MANAGER.logger.debug(`marked word: ${word}`);
}
}
}
}
if (_EXT_MANAGER.help) {
await _EXT_MANAGER.help.searchKeyword(word);
return;
}
} catch (error) {
const ex: Error = error;
_EXT_MANAGER.outputchannel.appendLine(`error during launching Qt help: ${ex.message}`);
vscode.window.showErrorMessage(`error launching Qt help: ${ex.message}`);
}
}
});

_EXT_MANAGER.registerCommand('qttools.searchonlinehelp', async () => {
if (_EXT_MANAGER && _EXT_MANAGER.qtManager) {
await _EXT_MANAGER.updateState();
try {
const query = await vscode.window.showInputBox({
value: '',
placeHolder: 'enter search term',
validateInput: text => {
return text === "" ? "search term could not be empty" : null;
}
});
if (query && _EXT_MANAGER.help) {
await _EXT_MANAGER.help.displaySearchResults(query);
return;
}
}
catch (error) {
const ex: Error = error;
_EXT_MANAGER.outputchannel.appendLine(`error query Qt help: ${ex.message}`);
vscode.window.showErrorMessage(`error query Qt help: ${ex.message}`);
}
}
});

_EXT_MANAGER.registerCommand('qttools.launchassistant', async () => {
if (_EXT_MANAGER && _EXT_MANAGER.qtManager) {
await _EXT_MANAGER.updateState();
Expand Down
100 changes: 100 additions & 0 deletions src/help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as vscode from 'vscode';

export class QtHelp implements vscode.Disposable {
private _webview: vscode.WebviewPanel | undefined;
private _context: vscode.ExtensionContext;
public useExternalBrowser = false;

constructor(public readonly extensionContext: vscode.ExtensionContext) {
this._context = extensionContext;
}

private async displayUrl(url: string) {
if (this.useExternalBrowser) {
vscode.env.openExternal(vscode.Uri.parse(url));
}
else {
try {

const content = await this.getWebViewContent(url);
if (this._webview) {
this._webview.reveal(vscode.ViewColumn.Beside);
}
else {
this._webview = vscode.window.createWebviewPanel(
'docs',
'Qt Online help',
{
viewColumn: vscode.ViewColumn.Beside,
preserveFocus: false
},
{
enableScripts: true,
enableFindWidget: true,
retainContextWhenHidden: true
}
);
this._webview.onDidDispose(() => {
this._webview = undefined;
}, null, this._context.subscriptions);
}
this._webview.webview.html = content;
}
catch (error) {
if ((error as Error).message !== "") {
vscode.window.showInformationMessage((error as Error).message);
}
}
}
}

public async displaySearchResults(text: string) {
const safeparam = encodeURIComponent(text);
const url = `https://doc.qt.io/qt-5/search-results.html?q=${safeparam}`;
await this.displayUrl(url);
}

public async searchKeyword(text: string) {
let url = "https://doc.qt.io/qt-5/";
if (text) {
const term = text.toLowerCase();
url += term + ".html";
}
await this.displayUrl(url);
}

private async getWebViewContent(url: string): Promise<string> {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>C++ Reference</title>
<style>
body, html
{
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
background-color: #fff;
}
iframe
{
border: 0px;
}
</style>
</head>
<body>
<iframe src="${url}" width="100%" height="100%" ></iframe>
</body>
</html>`;
}

dispose() {
if (this._webview) {
this._webview.dispose();
}
}
}
4 changes: 2 additions & 2 deletions src/qt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,13 @@ export class Qt {
this._creatorFilename = value;
}

public async launchAssistant() {
public async launchAssistant(args: string[] = []) {
this.outputchannel.appendLine(`launch assistant process`);
const assistantFilename = await this.assistantFilename();
if (!await tools.fileExists(assistantFilename)) {
throw new Error(`qt assistant executable does not exist '${assistantFilename}'`);
}
const assistant = spawn(assistantFilename, []);
const assistant = spawn(assistantFilename, args);
assistant.on('close', (code) => {
this.outputchannel.appendLine(`qt assistant child process exited with code ${code}`);
});
Expand Down