Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ab933fc
Add 'show next/previous alert' commands
asgerf Oct 5, 2022
2949fc3
Replace 'expanded' with a Set<number>
asgerf Oct 5, 2022
bb61b5e
Replace the expansion index with the result key
asgerf Oct 5, 2022
3c4682e
Ensure nodes are expanded
asgerf Oct 5, 2022
20dea5e
Also show selection in raw result view
asgerf Oct 5, 2022
125f638
Make raw result view respond to navigation events
asgerf Oct 5, 2022
88bfd19
Switch commands to up/down/left/right semantics
asgerf Oct 6, 2022
0f6100c
Bugfix in getPathNode
asgerf Oct 7, 2022
5a69465
Rename command IDs.
asgerf Oct 7, 2022
f759eed
Remove unsed parts of result-keys.ts
asgerf Oct 7, 2022
4871728
Added change note
asgerf Oct 10, 2022
d08e005
When stepping up or down, collapse the previous node
asgerf Oct 11, 2022
45b6288
Reveal panel on navigate, to prevent webview destruction
asgerf Oct 19, 2022
0e3679d
Scroll selected item into view
asgerf Oct 19, 2022
ecc07a5
Update extensions/ql-vscode/CHANGELOG.md
asgerf Oct 21, 2022
cbf15e6
Update extensions/ql-vscode/src/view/results/alert-table.tsx
asgerf Oct 21, 2022
53bb9d7
Title-case command names, like other commands
asgerf Oct 21, 2022
65777b5
Use null-aware accessors in getResult
asgerf Oct 21, 2022
d4a58a6
Consistently check for undefined rather than nullish
asgerf Oct 21, 2022
e1a56dd
Update a new more nullish checks
asgerf Oct 21, 2022
bdf7208
Mention keyboard navigation in README
asgerf Oct 21, 2022
9cb4b9d
Update extensions/ql-vscode/package.json
asgerf Oct 24, 2022
0acf9f7
Fix bad suggestion merge in package.json
asgerf Oct 24, 2022
ead1fb4
Merge branch 'main' into asgerf/navigate-alerts
asgerf Oct 24, 2022
b480f8f
Fix incorrect merge resolution in changelog
asgerf Oct 24, 2022
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
1 change: 1 addition & 0 deletions extensions/ql-vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [UNRELEASED]

- Fix a bug where it was not possible to add a database folder if the folder name starts with `db-`. [#1565](https://github.com/github/vscode-codeql/pull/1565)
- Added commands for navigating up, down, left, or right in the result viewer. Previously there were only commands for moving up and down the currently-selected path. We suggest binding keyboard shortcuts to these commands, for navigating the result viewer using the keyboard.
Comment thread
asgerf marked this conversation as resolved.
Outdated

## 1.7.0 - 20 September 2022

Expand Down
16 changes: 12 additions & 4 deletions extensions/ql-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -594,12 +594,20 @@
"title": "Copy Repository List"
},
{
"command": "codeQLQueryResults.nextPathStep",
"title": "CodeQL: Show Next Step on Path"
"command": "codeQLQueryResults.down",
"title": "CodeQL: Navigate down in result viewer"
},
{
"command": "codeQLQueryResults.previousPathStep",
"title": "CodeQL: Show Previous Step on Path"
"command": "codeQLQueryResults.up",
"title": "CodeQL: Navigate up in result viewer"
},
{
"command": "codeQLQueryResults.right",
"title": "CodeQL: Navigate right in result viewer"
},
{
"command": "codeQLQueryResults.left",
"title": "CodeQL: Navigate left in result viewer"
},
{
"command": "codeQL.restartQueryServer",
Expand Down
41 changes: 26 additions & 15 deletions extensions/ql-vscode/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
ALERTS_TABLE_NAME,
GRAPH_TABLE_NAME,
RawResultsSortState,
NavigationDirection,
} from './pure/interface-types';
import { Logger } from './logging';
import { commandRunner } from './commandRunner';
Expand Down Expand Up @@ -141,19 +142,24 @@ export class ResultsView extends AbstractWebview<IntoResultsViewMsg, FromResults
this.handleSelectionChange.bind(this)
)
);
void logger.log('Registering path-step navigation commands.');
this.push(
commandRunner(
'codeQLQueryResults.nextPathStep',
this.navigatePathStep.bind(this, 1)
)
);
this.push(
commandRunner(
'codeQLQueryResults.previousPathStep',
this.navigatePathStep.bind(this, -1)
)
);
const navigationCommands = {
'codeQLQueryResults.up': NavigationDirection.up,
'codeQLQueryResults.down': NavigationDirection.down,
'codeQLQueryResults.left': NavigationDirection.left,
'codeQLQueryResults.right': NavigationDirection.right,
// For backwards compatibility with keybindings set using an earlier version of the extension.
'codeQLQueryResults.nextPathStep': NavigationDirection.down,
'codeQLQueryResults.previousPathStep': NavigationDirection.up,
};
void logger.log('Registering result view navigation commands.');
for (const [commandId, direction] of Object.entries(navigationCommands)) {
this.push(
commandRunner(
commandId,
this.navigateResultView.bind(this, direction)
)
);
}

this.push(
this.databaseManager.onDidChangeDatabaseItem(({ kind }) => {
Expand All @@ -169,8 +175,13 @@ export class ResultsView extends AbstractWebview<IntoResultsViewMsg, FromResults
);
}

async navigatePathStep(direction: number): Promise<void> {
await this.postMessage({ t: 'navigatePath', direction });
async navigateResultView(direction: NavigationDirection): Promise<void> {
if (!this.panel?.visible) {
return;
}
// Reveal the panel now as the subsequent call to 'Window.showTextEditor' in 'showLocation' may destroy the webview otherwise.
this.panel.reveal();
await this.postMessage({ t: 'navigate', direction });
}

protected getPanelConfig(): WebviewPanelConfig {
Expand Down
17 changes: 11 additions & 6 deletions extensions/ql-vscode/src/pure/interface-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,17 @@ export interface ShowInterpretedPageMsg {
queryPath: string;
}

/** Advance to the next or previous path no in the path viewer */
export interface NavigatePathMsg {
t: 'navigatePath';
export const enum NavigationDirection {
up = 'up',
down = 'down',
left = 'left',
right = 'right',
}

/** 1 for next, -1 for previous */
direction: number;
/** Move up, down, left, or right in the result viewer. */
export interface NavigateMsg {
t: 'navigate';
direction: NavigationDirection;
}

/**
Expand All @@ -164,7 +169,7 @@ export type IntoResultsViewMsg =
| ResultsUpdatingMsg
| SetStateMsg
| ShowInterpretedPageMsg
| NavigatePathMsg
| NavigateMsg
| UntoggleShowProblemsMsg;

/**
Expand Down
47 changes: 29 additions & 18 deletions extensions/ql-vscode/src/pure/result-keys.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
import * as sarif from 'sarif';

/**
* Identifies a result, a path, or one of the nodes on a path.
*/
interface ResultKeyBase {
resultIndex: number;
pathIndex?: number;
pathNodeIndex?: number;
}

/**
* Identifies one of the results in a result set by its index in the result list.
*/
export interface Result {
export interface Result extends ResultKeyBase {
resultIndex: number;
pathIndex?: undefined;
pathNodeIndex?: undefined;
}

/**
* Identifies one of the paths associated with a result.
*/
export interface Path extends Result {
export interface Path extends ResultKeyBase {
pathIndex: number;
pathNodeIndex?: undefined;
}

/**
* Identifies one of the nodes in a path.
*/
export interface PathNode extends Path {
export interface PathNode extends ResultKeyBase {
pathIndex: number;
pathNodeIndex: number;
}

/** Alias for `undefined` but more readable in some cases */
export const none: PathNode | undefined = undefined;
export type ResultKey = Result | Path | PathNode;

/**
* Looks up a specific result in a result set.
*/
export function getResult(sarif: sarif.Log, key: Result): sarif.Result | undefined {
export function getResult(sarif: sarif.Log, key: Result | Path | PathNode): sarif.Result | undefined {
Comment thread
aeisenberg marked this conversation as resolved.
if (sarif.runs.length === 0) return undefined;
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.

Minor: The body of this function can be replaced with this:

  return sarif.runs?.[0]?.results?.[key.resultIndex];

It's really outside the scope of this PR, but it's a minor readability improvement if you want to add it.

if (sarif.runs[0].results === undefined) return undefined;
const results = sarif.runs[0].results;
Expand All @@ -37,7 +49,7 @@ export function getResult(sarif: sarif.Log, key: Result): sarif.Result | undefin
/**
* Looks up a specific path in a result set.
*/
export function getPath(sarif: sarif.Log, key: Path): sarif.ThreadFlow | undefined {
export function getPath(sarif: sarif.Log, key: Path | PathNode): sarif.ThreadFlow | undefined {
Comment thread
aeisenberg marked this conversation as resolved.
const result = getResult(sarif, key);
if (result === undefined) return undefined;
let index = -1;
Expand All @@ -58,22 +70,13 @@ export function getPath(sarif: sarif.Log, key: Path): sarif.ThreadFlow | undefin
export function getPathNode(sarif: sarif.Log, key: PathNode): sarif.Location | undefined {
const path = getPath(sarif, key);
if (path === undefined) return undefined;
return path.locations[key.pathNodeIndex];
}

/**
* Returns true if the two keys are both `undefined` or contain the same set of indices.
*/
export function equals(key1: PathNode | undefined, key2: PathNode | undefined): boolean {
if (key1 === key2) return true;
if (key1 === undefined || key2 === undefined) return false;
return key1.resultIndex === key2.resultIndex && key1.pathIndex === key2.pathIndex && key1.pathNodeIndex === key2.pathNodeIndex;
return path.locations[key.pathNodeIndex]?.location;
}

/**
* Returns true if the two keys contain the same set of indices and neither are `undefined`.
*/
export function equalsNotUndefined(key1: PathNode | undefined, key2: PathNode | undefined): boolean {
export function equalsNotUndefined(key1: Partial<PathNode> | undefined, key2: Partial<PathNode> | undefined): boolean {
if (key1 === undefined || key2 === undefined) return false;
return key1.resultIndex === key2.resultIndex && key1.pathIndex === key2.pathIndex && key1.pathNodeIndex === key2.pathNodeIndex;
}
Expand All @@ -93,3 +96,11 @@ export function getAllPaths(result: sarif.Result): sarif.ThreadFlow[] {
}
return paths;
}

/**
* Creates a unique string representation of the given key, suitable for use
* as the key in a map or set.
*/
export function keyToString(key: ResultKey) {
return key.resultIndex + '-' + (key.pathIndex ?? '') + '-' + (key.pathNodeIndex ?? '');
}
26 changes: 17 additions & 9 deletions extensions/ql-vscode/src/view/results/RawTableRow.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
import * as React from 'react';
import { ResultRow } from '../../pure/bqrs-cli-types';
import { zebraStripe } from './result-table-utils';
import { selectedRowClassName, zebraStripe } from './result-table-utils';
import RawTableValue from './RawTableValue';
import { ScrollIntoViewHelper } from './scroll-into-view-helper';

interface Props {
rowIndex: number;
row: ResultRow;
databaseUri: string;
className?: string;
selectedColumn?: number;
onSelected?: (row: number, column: number) => void;
scroller?: ScrollIntoViewHelper;
}

export default function RawTableRow(props: Props) {
return (
<tr key={props.rowIndex} {...zebraStripe(props.rowIndex, props.className || '')}>
<td key={-1}>{props.rowIndex + 1}</td>

{props.row.map((value, columnIndex) => (
<td key={columnIndex}>
<RawTableValue
value={value}
databaseUri={props.databaseUri}
/>
</td>
))}
{props.row.map((value, columnIndex) => {
const isSelected = props.selectedColumn === columnIndex;
return (
<td ref={props.scroller?.ref(isSelected)} key={columnIndex} {...isSelected ? { className: selectedRowClassName } : {}}>
<RawTableValue
value={value}
databaseUri={props.databaseUri}
onSelected={() => props.onSelected?.(props.rowIndex, columnIndex)}
/>
</td>
);
})}
</tr>
);
}
3 changes: 2 additions & 1 deletion extensions/ql-vscode/src/view/results/RawTableValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CellValue } from '../../pure/bqrs-cli-types';
interface Props {
value: CellValue;
databaseUri: string;
onSelected?: () => void;
}

export default function RawTableValue(props: Props): JSX.Element {
Expand All @@ -18,5 +19,5 @@ export default function RawTableValue(props: Props): JSX.Element {
return <span>{renderLocation(undefined, rawValue.toString())}</span>;
}

return renderLocation(rawValue.url, rawValue.label, props.databaseUri);
return renderLocation(rawValue.url, rawValue.label, props.databaseUri, undefined, props.onSelected);
}
Loading