Skip to content
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
24 changes: 23 additions & 1 deletion extensions/ql-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import {
env,
window,
QuickPickItem,
Range
Range,
workspace,
ProviderResult
} from 'vscode';
import { LanguageClient } from 'vscode-languageclient';
import * as os from 'os';
Expand Down Expand Up @@ -78,6 +80,7 @@ import { CodeQlStatusBarHandler } from './status-bar';
import { Credentials } from './authentication';
import { RemoteQueriesManager } from './remote-queries/remote-queries-manager';
import { RemoteQuery } from './remote-queries/remote-query';
import { URLSearchParams } from 'url';

/**
* extension.ts
Expand Down Expand Up @@ -773,6 +776,8 @@ async function activateWithInstalledDistribution(
void logger.log('Initializing remote queries interface.');
const rqm = new RemoteQueriesManager(ctx, logger, cliServer);

registerRemoteQueryTextProvider();

// The "runRemoteQuery" command is internal-only.
ctx.subscriptions.push(
commandRunnerWithProgress('codeQL.runRemoteQuery', async (
Expand Down Expand Up @@ -980,3 +985,20 @@ async function initializeLogging(ctx: ExtensionContext): Promise<void> {
}

const checkForUpdatesCommand = 'codeQL.checkForUpdatesToCLI';

/**
* This text provider lets us open readonly files in the editor.
*
* TODO: Consolidate this with the 'codeql' text provider in query-history.ts.
*/
function registerRemoteQueryTextProvider() {
workspace.registerTextDocumentContentProvider('remote-query', {
provideTextDocumentContent(
uri: Uri
): ProviderResult<string> {
const params = new URLSearchParams(uri.query);

return params.get('queryText');
},
});
}
9 changes: 8 additions & 1 deletion extensions/ql-vscode/src/pure/interface-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ export interface OpenFileMsg {
filePath: string;
}

export interface OpenVirtualFileMsg {
t: 'openVirtualFile';
queryText: string;
}

/**
* Message from the results view to toggle the display of
* query diagnostics.
Expand Down Expand Up @@ -368,7 +373,9 @@ export interface ParsedResultSets {

export type FromRemoteQueriesMessage =
| RemoteQueryLoadedMessage
| RemoteQueryErrorMessage;
| RemoteQueryErrorMessage
| OpenFileMsg
| OpenVirtualFileMsg;

export type ToRemoteQueriesMessage =
| SetRemoteQueryResultMessage;
Expand Down
2 changes: 1 addition & 1 deletion extensions/ql-vscode/src/query-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export type QueryHistoryItemOptions = {
isQuickQuery?: boolean;
};

const SHOW_QUERY_TEXT_MSG = `\
export const SHOW_QUERY_TEXT_MSG = `\
////////////////////////////////////////////////////////////////////////////////////
// This is the text of the entire query file when it was executed for this query //
// run. The text or dependent libraries may have changed since then. //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
window as Window,
ViewColumn,
Uri,
workspace,
} from 'vscode';
import * as path from 'path';

Expand All @@ -19,6 +20,9 @@ import { AnalysisResult, RemoteQueryResult } from './remote-query-result';
import { RemoteQuery } from './remote-query';
import { RemoteQueryResult as RemoteQueryResultViewModel } from './shared/remote-query-result';
import { AnalysisResult as AnalysisResultViewModel } from './shared/remote-query-result';
import { showAndLogWarningMessage } from '../helpers';
import { URLSearchParams } from 'url';
import { SHOW_QUERY_TEXT_MSG } from '../query-history';

export class RemoteQueriesInterfaceManager {
private panel: WebviewPanel | undefined;
Expand Down Expand Up @@ -53,15 +57,17 @@ export class RemoteQueriesInterfaceManager {
* @returns A fully created view model.
*/
private buildViewModel(query: RemoteQuery, queryResult: RemoteQueryResult): RemoteQueryResultViewModel {
const queryFile = path.basename(query.queryFilePath);
const queryFileName = path.basename(query.queryFilePath);
const totalResultCount = queryResult.analysisResults.reduce((acc, cur) => acc + cur.resultCount, 0);
const executionDuration = this.getDuration(queryResult.executionEndTime, query.executionStartTime);
const analysisResults = this.buildAnalysisResults(queryResult.analysisResults);
const affectedRepositories = queryResult.analysisResults.filter(r => r.resultCount > 0);

return {
queryTitle: query.queryName,
queryFile: queryFile,
queryFileName: queryFileName,
queryFilePath: query.queryFilePath,
queryText: query.queryText,
totalRepositoryCount: query.repositories.length,
affectedRepositoryCount: affectedRepositories.length,
totalResultCount: totalResultCount,
Expand Down Expand Up @@ -129,6 +135,31 @@ export class RemoteQueriesInterfaceManager {
});
}

private async openFile(filePath: string) {
try {
const textDocument = await workspace.openTextDocument(filePath);
await Window.showTextDocument(textDocument, ViewColumn.One);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Eventually, it would be nice to do something like #1037, but that can happen later. Once #1037 gets in, we can work on extracting the behaviour so it is used wherever we are opening editors.

} catch (error) {
void showAndLogWarningMessage(`Could not open file: ${filePath}`);
}
}

private async openVirtualFile(text: string) {
try {
const params = new URLSearchParams({
queryText: encodeURIComponent(SHOW_QUERY_TEXT_MSG + text)
});
const uri = Uri.parse(
`remote-query:query-text.ql?${params.toString()}`,
true
);
const doc = await workspace.openTextDocument(uri);
await Window.showTextDocument(doc, { preview: false });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this read-only? It's possible to make happen, but I forget how.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Indeed, it's read-only! That's what the remote-query prefix and the corresponding provideTextDocumentContent function do 😃

} catch (error) {
void showAndLogWarningMessage('Could not open query text');
}
}

private async handleMsgFromView(
msg: FromRemoteQueriesMessage
): Promise<void> {
Expand All @@ -143,6 +174,12 @@ export class RemoteQueriesInterfaceManager {
`Remote query error: ${msg.error}`
);
break;
case 'openFile':
await this.openFile(msg.filePath);
break;
case 'openVirtualFile':
await this.openVirtualFile(msg.queryText);
break;
default:
assertNever(msg);
}
Expand Down
1 change: 1 addition & 0 deletions extensions/ql-vscode/src/remote-queries/remote-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Repository } from './repository';
export interface RemoteQuery {
queryName: string;
queryFilePath: string;
queryText: string;
controllerRepository: Repository;
repositories: Repository[];
executionStartTime: Date;
Expand Down
9 changes: 6 additions & 3 deletions extensions/ql-vscode/src/remote-queries/run-remote-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ export async function runRemoteQuery(
return;
}

const remoteQuery = buildRemoteQueryEntity(repositories, queryFile, owner, repo, queryStartTime, workflowRunId);
const remoteQuery = await buildRemoteQueryEntity(repositories, queryFile, owner, repo, queryStartTime, workflowRunId);

// don't return the path because it has been deleted
return { query: remoteQuery };
Expand Down Expand Up @@ -451,14 +451,14 @@ async function ensureNameAndSuite(queryPackDir: string, packRelativePath: string
await fs.writeFile(packPath, yaml.safeDump(qlpack));
}

function buildRemoteQueryEntity(
async function buildRemoteQueryEntity(
repositories: string[],
queryFilePath: string,
controllerRepoOwner: string,
controllerRepoName: string,
queryStartTime: Date,
workflowRunId: number
): RemoteQuery {
): Promise<RemoteQuery> {
// For now, just use the file name as the query name.
const queryName = path.basename(queryFilePath);

Expand All @@ -467,9 +467,12 @@ function buildRemoteQueryEntity(
return { owner: owner, name: repo };
});

const queryText = await fs.readFile(queryFilePath, 'utf8');

return {
queryName,
queryFilePath,
queryText,
controllerRepository: {
owner: controllerRepoOwner,
name: controllerRepoName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export interface RemoteQueryResult {
queryTitle: string;
queryFile: string;
queryFileName: string;
queryFilePath: string;
queryText: string;
totalRepositoryCount: number;
affectedRepositoryCount: number;
totalResultCount: number;
Expand Down
30 changes: 27 additions & 3 deletions extensions/ql-vscode/src/remote-queries/view/RemoteQueries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const numOfReposInContractedMode = 10;

const emptyQueryResult: RemoteQueryResult = {
queryTitle: '',
queryFile: '',
queryFileName: '',
queryFilePath: '',
queryText: '',
totalRepositoryCount: 0,
affectedRepositoryCount: 0,
totalResultCount: 0,
Expand Down Expand Up @@ -63,6 +65,20 @@ export function RemoteQueries(): JSX.Element {
const [repoListExpanded, setRepoListExpanded] = useState(false);
const numOfReposToShow = repoListExpanded ? queryResult.results.length : numOfReposInContractedMode;

const openQueryFile = () => {
vscode.postMessage({
t: 'openFile',
filePath: queryResult.queryFilePath
});
};

const openQueryTextVirtualFile = () => {
vscode.postMessage({
t: 'openVirtualFile',
queryText: queryResult.queryText
});
};

try {
return <div className="vscode-codeql__remote-queries-view">
<h1 className="vscode-codeql__query-title">{queryResult.queryTitle}</h1>
Expand All @@ -72,8 +88,16 @@ export function RemoteQueries(): JSX.Element {
({queryResult.executionDuration}), {queryResult.executionTimestamp}
</p>
<p className="vscode-codeql__paragraph">
<span className="vscode-codeql__query-file">{octicons.file} <span>{queryResult.queryFile}</span></span>
<span>{octicons.codeSquare} <span>query</span></span>
<span className="vscode-codeql__query-file">{octicons.file}
<a className="vscode-codeql__query-file-link" href="#" onClick={openQueryFile}>
{queryResult.queryFileName}
</a>
</span>
<span>{octicons.codeSquare}
<a className="vscode-codeql__query-file-link" href="#" onClick={openQueryTextVirtualFile}>
query
</a>
</span>
</p>

<div className="vscode-codeql__query-summary-container">
Expand Down
10 changes: 10 additions & 0 deletions extensions/ql-vscode/src/remote-queries/view/remoteQueries.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@
padding-right: 1em;
}

.vscode-codeql__query-file-link {
text-decoration: none;
padding-left: 0.3em;
color: var(--vscode-editor-foreground);
}

.vscode-codeql__query-file-link:hover {
color: var(--vscode-editor-foreground);
}

.vscode-codeql__query-summary-container {
padding-top: 1.5em;
}
Expand Down