Merge pull request #491 from jcreedcmu/jcreed/cleanup
Remove pagination feature flag
This commit is contained in:
@@ -103,30 +103,7 @@ export function adaptBqrs(schema: AdaptedSchema, page: DecodedBqrsChunk): RawRes
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This type has two branches; we are in the process of changing from
|
||||
* one to the other. The old way is to parse them inside the webview,
|
||||
* the new way is to parse them in the extension. The main motivation
|
||||
* for this transition is to make pagination possible in such a way
|
||||
* that only one page needs to be sent from the extension to the webview.
|
||||
*/
|
||||
export type ParsedResultSets = ExtensionParsedResultSets | WebviewParsedResultSets;
|
||||
|
||||
/**
|
||||
* The old method doesn't require any nontrivial information to be included here,
|
||||
* just a tag to indicate that it is being used.
|
||||
*/
|
||||
export interface WebviewParsedResultSets {
|
||||
t: 'WebviewParsed';
|
||||
selectedTable?: string; // when undefined, means 'show default table'
|
||||
}
|
||||
|
||||
/**
|
||||
* The new method includes which bqrs page is being sent, and the
|
||||
* actual results parsed on the extension side.
|
||||
*/
|
||||
export interface ExtensionParsedResultSets {
|
||||
t: 'ExtensionParsed';
|
||||
export interface ParsedResultSets {
|
||||
pageNumber: number;
|
||||
numPages: number;
|
||||
numInterpretedPages: number;
|
||||
|
||||
@@ -39,18 +39,6 @@ class Setting {
|
||||
|
||||
const ROOT_SETTING = new Setting('codeQL');
|
||||
|
||||
// Enable experimental features
|
||||
|
||||
/**
|
||||
* Any settings below are deliberately not in package.json so that
|
||||
* they do not appear in the settings ui in vscode itself. If users
|
||||
* want to enable experimental features, they can add them directly in
|
||||
* their vscode settings json file.
|
||||
*/
|
||||
|
||||
/* Advanced setting: used to enable bqrs parsing in the cli instead of in the webview. */
|
||||
export const EXPERIMENTAL_BQRS_SETTING = new Setting('experimentalBqrsParsing', ROOT_SETTING);
|
||||
|
||||
// Distribution configuration
|
||||
|
||||
const DISTRIBUTION_SETTING = new Setting('cli', ROOT_SETTING);
|
||||
|
||||
@@ -41,7 +41,6 @@ import {
|
||||
ParsedResultSets,
|
||||
RawResultSet,
|
||||
} from './adapt';
|
||||
import { EXPERIMENTAL_BQRS_SETTING } from './config';
|
||||
import {
|
||||
WebviewReveal,
|
||||
fileUriToWebviewUri,
|
||||
@@ -335,40 +334,33 @@ export class InterfaceManager extends DisposableObject {
|
||||
}
|
||||
|
||||
const getParsedResultSets = async (): Promise<ParsedResultSets> => {
|
||||
if (EXPERIMENTAL_BQRS_SETTING.getValue()) {
|
||||
const resultSetSchemas = await this.getResultSetSchemas(results);
|
||||
const resultSetNames = resultSetSchemas.map(schema => schema.name);
|
||||
|
||||
// This may not wind up being the page we actually show, if there are interpreted results,
|
||||
// but speculatively send it anyway.
|
||||
const selectedTable = getDefaultResultSetName(resultSetNames);
|
||||
const schema = resultSetSchemas.find(
|
||||
(resultSet) => resultSet.name == selectedTable
|
||||
)!;
|
||||
if (schema === undefined) {
|
||||
return { t: 'WebviewParsed' };
|
||||
}
|
||||
const resultSetSchemas = await this.getResultSetSchemas(results);
|
||||
const resultSetNames = resultSetSchemas.map(schema => schema.name);
|
||||
|
||||
const chunk = await this.cliServer.bqrsDecode(
|
||||
results.query.resultsPaths.resultsPath,
|
||||
schema.name,
|
||||
RAW_RESULTS_PAGE_SIZE,
|
||||
schema.pagination?.offsets[0]
|
||||
);
|
||||
const adaptedSchema = adaptSchema(schema);
|
||||
const resultSet = adaptBqrs(adaptedSchema, chunk);
|
||||
return {
|
||||
t: 'ExtensionParsed',
|
||||
pageNumber: 0,
|
||||
numPages: numPagesOfResultSet(resultSet),
|
||||
numInterpretedPages: numInterpretedPages(this._interpretation),
|
||||
resultSet: { t: 'RawResultSet', ...resultSet },
|
||||
selectedTable: undefined,
|
||||
resultSetNames,
|
||||
};
|
||||
} else {
|
||||
return { t: 'WebviewParsed' };
|
||||
}
|
||||
// This may not wind up being the page we actually show, if there are interpreted results,
|
||||
// but speculatively send it anyway.
|
||||
const selectedTable = getDefaultResultSetName(resultSetNames);
|
||||
const schema = resultSetSchemas.find(
|
||||
(resultSet) => resultSet.name == selectedTable
|
||||
)!;
|
||||
|
||||
const chunk = await this.cliServer.bqrsDecode(
|
||||
results.query.resultsPaths.resultsPath,
|
||||
schema.name,
|
||||
RAW_RESULTS_PAGE_SIZE,
|
||||
schema.pagination?.offsets[0]
|
||||
);
|
||||
const adaptedSchema = adaptSchema(schema);
|
||||
const resultSet = adaptBqrs(adaptedSchema, chunk);
|
||||
return {
|
||||
pageNumber: 0,
|
||||
numPages: numPagesOfResultSet(resultSet),
|
||||
numInterpretedPages: numInterpretedPages(this._interpretation),
|
||||
resultSet: { t: 'RawResultSet', ...resultSet },
|
||||
selectedTable: undefined,
|
||||
resultSetNames,
|
||||
};
|
||||
};
|
||||
|
||||
await this.postMessage({
|
||||
@@ -461,7 +453,6 @@ export class InterfaceManager extends DisposableObject {
|
||||
const resultSet = adaptBqrs(adaptedSchema, chunk);
|
||||
|
||||
const parsedResultSets: ParsedResultSets = {
|
||||
t: 'ExtensionParsed',
|
||||
pageNumber,
|
||||
resultSet: { t: 'RawResultSet', ...resultSet },
|
||||
numPages: numPagesOfResultSet(resultSet),
|
||||
|
||||
@@ -30,7 +30,7 @@ export class RawTable extends React.Component<RawTableProps, {}> {
|
||||
const tableRows = dataRows.map((row: ResultRow, rowIndex: number) =>
|
||||
<RawTableRow
|
||||
key={rowIndex}
|
||||
rowIndex={rowIndex}
|
||||
rowIndex={rowIndex + this.props.offset}
|
||||
row={row}
|
||||
databaseUri={databaseUri}
|
||||
/>
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { PathTable } from './alert-table';
|
||||
import { RawTable } from './raw-results-table';
|
||||
import { ResultTableProps, tableSelectionHeaderClassName, toggleDiagnosticsClassName, alertExtrasClassName } from './result-table-utils';
|
||||
import { ParsedResultSets, ExtensionParsedResultSets } from '../adapt';
|
||||
import { ParsedResultSets } from '../adapt';
|
||||
import { vscode } from './vscode-api';
|
||||
|
||||
|
||||
@@ -90,52 +90,23 @@ export class ResultTables
|
||||
}
|
||||
|
||||
private getResultSetNames(resultSets: ResultSet[]): string[] {
|
||||
if (this.props.parsedResultSets.t === 'ExtensionParsed') {
|
||||
return this.props.parsedResultSets.resultSetNames.concat([ALERTS_TABLE_NAME]);
|
||||
}
|
||||
else {
|
||||
return resultSets.map(resultSet => resultSet.schema.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we have a result set obtained from the extension that came
|
||||
* from the ExtensionParsed branch of ParsedResultSets. This is evidence
|
||||
* that the user has the experimental flag turned on that allows extension-side
|
||||
* bqrs parsing.
|
||||
*/
|
||||
paginationAllowed(): boolean {
|
||||
return this.props.parsedResultSets.t === 'ExtensionParsed';
|
||||
return this.props.parsedResultSets.resultSetNames.concat([ALERTS_TABLE_NAME]);
|
||||
}
|
||||
|
||||
constructor(props: ResultTablesProps) {
|
||||
super(props);
|
||||
const selectedTable = props.parsedResultSets.selectedTable || getDefaultResultSet(this.getResultSets());
|
||||
let selectedPage: string;
|
||||
|
||||
switch (props.parsedResultSets.t) {
|
||||
case 'ExtensionParsed':
|
||||
selectedPage = (props.parsedResultSets.pageNumber + 1) + '';
|
||||
break;
|
||||
case 'WebviewParsed':
|
||||
selectedPage = '';
|
||||
break;
|
||||
}
|
||||
const selectedPage = (props.parsedResultSets.pageNumber + 1) + '';
|
||||
this.state = { selectedTable, selectedPage };
|
||||
}
|
||||
|
||||
private onTableSelectionChange = (event: React.ChangeEvent<HTMLSelectElement>): void => {
|
||||
const selectedTable = event.target.value;
|
||||
|
||||
if (this.paginationAllowed()) {
|
||||
vscode.postMessage({
|
||||
t: 'changePage',
|
||||
pageNumber: 0,
|
||||
selectedTable
|
||||
});
|
||||
}
|
||||
else
|
||||
this.setState({ selectedTable });
|
||||
vscode.postMessage({
|
||||
t: 'changePage',
|
||||
pageNumber: 0,
|
||||
selectedTable
|
||||
});
|
||||
}
|
||||
|
||||
private alertTableExtras(): JSX.Element | undefined {
|
||||
@@ -164,15 +135,11 @@ export class ResultTables
|
||||
|
||||
getOffset(): number {
|
||||
const { parsedResultSets } = this.props;
|
||||
switch (parsedResultSets.t) {
|
||||
case 'ExtensionParsed':
|
||||
return parsedResultSets.pageNumber * RAW_RESULTS_PAGE_SIZE;
|
||||
case 'WebviewParsed':
|
||||
return 0;
|
||||
}
|
||||
return parsedResultSets.pageNumber * RAW_RESULTS_PAGE_SIZE;
|
||||
}
|
||||
|
||||
renderPageButtons(resultSets: ExtensionParsedResultSets): JSX.Element {
|
||||
renderPageButtons(): JSX.Element {
|
||||
const { parsedResultSets } = this.props;
|
||||
const selectedTable = this.state.selectedTable;
|
||||
|
||||
// FIXME: The extension, not the view, should be in charge of deciding whether to initially show
|
||||
@@ -181,7 +148,7 @@ export class ResultTables
|
||||
// not interpreted pages, because the extension doesn't know the view will default to showing alerts
|
||||
// instead.
|
||||
const numPages = selectedTable == ALERTS_TABLE_NAME ?
|
||||
resultSets.numInterpretedPages : resultSets.numPages;
|
||||
parsedResultSets.numInterpretedPages : parsedResultSets.numPages;
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ selectedPage: e.target.value });
|
||||
@@ -201,14 +168,14 @@ export class ResultTables
|
||||
const prevPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
vscode.postMessage({
|
||||
t: 'changePage',
|
||||
pageNumber: Math.max(resultSets.pageNumber - 1, 0),
|
||||
pageNumber: Math.max(parsedResultSets.pageNumber - 1, 0),
|
||||
selectedTable,
|
||||
});
|
||||
};
|
||||
const nextPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
vscode.postMessage({
|
||||
t: 'changePage',
|
||||
pageNumber: Math.min(resultSets.pageNumber + 1, numPages - 1),
|
||||
pageNumber: Math.min(parsedResultSets.pageNumber + 1, numPages - 1),
|
||||
selectedTable,
|
||||
});
|
||||
};
|
||||
@@ -230,13 +197,6 @@ export class ResultTables
|
||||
</span>;
|
||||
}
|
||||
|
||||
renderButtons(): JSX.Element {
|
||||
if (this.props.parsedResultSets.t === 'ExtensionParsed' && this.paginationAllowed())
|
||||
return this.renderPageButtons(this.props.parsedResultSets);
|
||||
else
|
||||
return <span />;
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { selectedTable } = this.state;
|
||||
const resultSets = this.getResultSets();
|
||||
@@ -250,7 +210,7 @@ export class ResultTables
|
||||
resultSetNames.map(name => <option key={name} value={name}>{name}</option>);
|
||||
|
||||
return <div>
|
||||
{this.renderButtons()}
|
||||
{this.renderPageButtons()}
|
||||
<div className={tableSelectionHeaderClassName}>
|
||||
<select value={selectedTable} onChange={this.onTableSelectionChange}>
|
||||
{resultSetOptions}
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import * as Rdom from 'react-dom';
|
||||
import * as bqrs from 'semmle-bqrs';
|
||||
import {
|
||||
ElementBase,
|
||||
PrimitiveColumnValue,
|
||||
PrimitiveTypeKind,
|
||||
tryGetResolvableLocation,
|
||||
} from 'semmle-bqrs';
|
||||
import { assertNever } from '../helpers-pure';
|
||||
import {
|
||||
DatabaseInfo,
|
||||
@@ -22,8 +15,6 @@ import {
|
||||
import { EventHandlers as EventHandlerList } from './event-handler-list';
|
||||
import { ResultTables } from './result-tables';
|
||||
import {
|
||||
ResultValue,
|
||||
ResultRow,
|
||||
ParsedResultSets,
|
||||
} from '../adapt';
|
||||
import { ResultSet } from '../interface-types';
|
||||
@@ -36,91 +27,6 @@ import { vscode } from './vscode-api';
|
||||
* Displaying query results.
|
||||
*/
|
||||
|
||||
async function* getChunkIterator(
|
||||
response: Response
|
||||
): AsyncIterableIterator<Uint8Array> {
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to load results: (${response.status}) ${response.statusText}`
|
||||
);
|
||||
}
|
||||
const reader = response.body!.getReader();
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
yield value!;
|
||||
}
|
||||
}
|
||||
|
||||
function translatePrimitiveValue(
|
||||
value: PrimitiveColumnValue,
|
||||
type: PrimitiveTypeKind
|
||||
): ResultValue {
|
||||
switch (type) {
|
||||
case 'i':
|
||||
case 'f':
|
||||
case 's':
|
||||
case 'd':
|
||||
case 'b':
|
||||
return value.toString();
|
||||
|
||||
case 'u':
|
||||
return {
|
||||
uri: value as string,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function parseResultSets(
|
||||
response: Response
|
||||
): Promise<readonly ResultSet[]> {
|
||||
const chunks = getChunkIterator(response);
|
||||
|
||||
const resultSets: ResultSet[] = [];
|
||||
|
||||
await bqrs.parse(chunks, (resultSetSchema) => {
|
||||
const columnTypes = resultSetSchema.columns.map((column) => column.type);
|
||||
const rows: ResultRow[] = [];
|
||||
resultSets.push({
|
||||
t: 'RawResultSet',
|
||||
schema: resultSetSchema,
|
||||
rows: rows,
|
||||
});
|
||||
|
||||
return (tuple) => {
|
||||
const row: ResultValue[] = [];
|
||||
tuple.forEach((value, index) => {
|
||||
const type = columnTypes[index];
|
||||
if (type.type === 'e') {
|
||||
const element: ElementBase = value as ElementBase;
|
||||
const label =
|
||||
element.label !== undefined ? element.label : element.id.toString(); //REVIEW: URLs?
|
||||
const resolvableLocation = tryGetResolvableLocation(element.location);
|
||||
if (resolvableLocation !== undefined) {
|
||||
row.push({
|
||||
label: label,
|
||||
location: resolvableLocation,
|
||||
});
|
||||
} else {
|
||||
// No location link.
|
||||
row.push(label);
|
||||
}
|
||||
} else {
|
||||
row.push(
|
||||
translatePrimitiveValue(value as PrimitiveColumnValue, type.type)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
rows.push(row);
|
||||
};
|
||||
});
|
||||
|
||||
return resultSets;
|
||||
}
|
||||
|
||||
interface ResultsInfo {
|
||||
parsedResultSets: ParsedResultSets;
|
||||
resultsPath: string;
|
||||
@@ -200,7 +106,6 @@ class App extends React.Component<{}, ResultsViewState> {
|
||||
this.updateStateWithNewResultsInfo({
|
||||
resultsPath: '', // FIXME: Not used for interpreted, refactor so this is not needed
|
||||
parsedResultSets: {
|
||||
t: 'ExtensionParsed',
|
||||
numPages: msg.numPages,
|
||||
numInterpretedPages: msg.numPages,
|
||||
resultSetNames: msg.resultSetNames,
|
||||
@@ -269,13 +174,7 @@ class App extends React.Component<{}, ResultsViewState> {
|
||||
resultsInfo: ResultsInfo
|
||||
): Promise<readonly ResultSet[]> {
|
||||
const parsedResultSets = resultsInfo.parsedResultSets;
|
||||
switch (parsedResultSets.t) {
|
||||
case 'WebviewParsed':
|
||||
return await this.fetchResultSets(resultsInfo);
|
||||
case 'ExtensionParsed': {
|
||||
return [{ t: 'RawResultSet', ...parsedResultSets.resultSet }];
|
||||
}
|
||||
}
|
||||
return [{ t: 'RawResultSet', ...parsedResultSets.resultSet }];
|
||||
}
|
||||
|
||||
private async loadResults(): Promise<void> {
|
||||
@@ -321,35 +220,6 @@ class App extends React.Component<{}, ResultsViewState> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This is deprecated, because it calls `fetch`. We are moving
|
||||
* towards doing all bqrs parsing in the extension.
|
||||
*/
|
||||
private async fetchResultSets(
|
||||
resultsInfo: ResultsInfo
|
||||
): Promise<readonly ResultSet[]> {
|
||||
const unsortedResponse = await fetch(resultsInfo.resultsPath);
|
||||
const unsortedResultSets = await parseResultSets(unsortedResponse);
|
||||
return Promise.all(
|
||||
unsortedResultSets.map(async (unsortedResultSet) => {
|
||||
const sortedResultSetInfo = resultsInfo.sortedResultsMap.get(
|
||||
unsortedResultSet.schema.name
|
||||
);
|
||||
if (sortedResultSetInfo === undefined) {
|
||||
return unsortedResultSet;
|
||||
}
|
||||
const response = await fetch(sortedResultSetInfo.resultsPath);
|
||||
const resultSets = await parseResultSets(response);
|
||||
if (resultSets.length != 1) {
|
||||
throw new Error(
|
||||
`Expected sorted BQRS to contain a single result set, encountered ${resultSets.length} result sets.`
|
||||
);
|
||||
}
|
||||
return resultSets[0];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private getSortStates(
|
||||
resultsInfo: ResultsInfo
|
||||
): Map<string, RawResultsSortState> {
|
||||
@@ -369,7 +239,7 @@ class App extends React.Component<{}, ResultsViewState> {
|
||||
displayedResults.resultsInfo !== null
|
||||
) {
|
||||
const parsedResultSets = displayedResults.resultsInfo.parsedResultSets;
|
||||
const key = (parsedResultSets.t === 'ExtensionParsed' ? (parsedResultSets.selectedTable || '') + parsedResultSets.pageNumber : '');
|
||||
const key = (parsedResultSets.selectedTable || '') + parsedResultSets.pageNumber;
|
||||
return (
|
||||
<ResultTables
|
||||
key={key}
|
||||
|
||||
Reference in New Issue
Block a user