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

Unescaping works not as intended and is slow #297

Merged
merged 11 commits into from
Jun 4, 2024
20 changes: 16 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
jobs:
build:
name: Yarn CI with Node ${{ matrix.node-version }}
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04

strategy:
matrix:
Expand All @@ -26,13 +26,25 @@ jobs:
node-version: ${{ matrix.node-version }}

- name: Install dependencies
run: yarn install
run: yarn install -D

- name: Validate Formatting
run: yarn run prettier:check

- name: Lint Extension
run: yarn run lint

- name: Build Extension
run: yarn run build

- name: Lint Extension
run: yarn run lint
- name: Prepare Test Runner
run: |
sudo apt-get install meson gjs gir1.2-gda-5.0 gir1.2-gsound-1.0 -y
git clone https://github.com/ptomato/jasmine-gjs
cd jasmine-gjs
meson setup build
meson compile -C build
sudo meson install -C build

- name: Run Tests
run: yarn run test
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,5 @@ fabric.properties

# ts-for-gjs output

docs-lock.json
docs-lock.json
build/
4 changes: 3 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.github/
.github/FUNDING.yml
.github/pull_request_template.md
.github/ISSUE_TEMPLATE/
node_modules/
*.md
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ Since Gnome 45 we had to break compatibility with previous Gnome versions, these
sudo dnf install libgda libgda-sqlite
```

- Arch Linux (For gnome-43 or later, you need libgda6)
- Arch Linux

```bash
sudo pacman -S libgda
sudo pacman -S libgda6
```

- Ubuntu/Debian
Expand Down
59 changes: 30 additions & 29 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"clean:schema": "rm -rf ./dist/schemas/*.compiled",
"build:package": "yarn run build && rm -rf './dist/[email protected]' && cd ./dist && zip -qr '[email protected]' .",
"watch": "yarn run build && yarn run rollup -c --watch",
"test": "echo \"Error: no test specified\" && exit 1",
"test": "jasmine --module --no-config build/tests/",
"lint": "eslint --ext .ts src/",
"shell:restart": "busctl --user call org.gnome.Shell /io/elhan/Pano io.elhan.Pano restart",
"build-and-restart": "yarn run build && yarn run shell:restart",
Expand All @@ -33,36 +33,38 @@
}
},
"devDependencies": {
"@commitlint/cli": "^19.2.2",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@girs/adw-1": "1.5.0-4.0.0-beta.2",
"@girs/clutter-14": "14.0.0-4.0.0-beta.2",
"@girs/gda-5.0": "5.0.0-4.0.0-beta.2",
"@girs/gda-6.0": "6.0.0-4.0.0-beta.2",
"@girs/gdk-4.0": "4.0.0-4.0.0-beta.2",
"@girs/gdkpixbuf-2.0": "2.0.0-4.0.0-beta.2",
"@girs/gio-2.0": "2.80.0-4.0.0-beta.2",
"@girs/glib-2.0": "2.80.0-4.0.0-beta.2",
"@girs/gnome-shell": "46.0.0-4.0.0-beta.3",
"@girs/gobject-2.0": "2.80.0-4.0.0-beta.2",
"@girs/graphene-1.0": "1.0.0-4.0.0-beta.2",
"@girs/gsound-1.0": "1.0.0-4.0.0-beta.2",
"@girs/gtk-4.0": "4.14.1-4.0.0-beta.2",
"@girs/meta-14": "14.0.0-4.0.0-beta.2",
"@girs/pango-1.0": "1.51.2-4.0.0-beta.2",
"@girs/shell-14": "14.0.0-4.0.0-beta.2",
"@girs/soup-3.0": "3.4.4-4.0.0-beta.2",
"@girs/st-14": "14.0.0-4.0.0-beta.2",
"@rollup/plugin-commonjs": "^25.0.7",
"@girs/adw-1": "1.5.0-4.0.0-beta.5",
"@girs/clutter-14": "14.0.0-4.0.0-beta.5",
"@girs/gda-5.0": "5.0.0-4.0.0-beta.5",
"@girs/gda-6.0": "6.0.0-4.0.0-beta.5",
"@girs/gdk-4.0": "4.0.0-4.0.0-beta.5",
"@girs/gdkpixbuf-2.0": "2.0.0-4.0.0-beta.5",
"@girs/gio-2.0": "2.80.0-4.0.0-beta.5",
"@girs/glib-2.0": "2.80.0-4.0.0-beta.5",
"@girs/gnome-shell": "46.0.0-beta9",
"@girs/gobject-2.0": "2.80.0-4.0.0-beta.5",
"@girs/graphene-1.0": "1.0.0-4.0.0-beta.5",
"@girs/gsound-1.0": "1.0.0-4.0.0-beta.5",
"@girs/gtk-4.0": "4.14.3-4.0.0-beta.5",
"@girs/meta-14": "14.0.0-4.0.0-beta.5",
"@girs/pango-1.0": "1.52.2-4.0.0-beta.5",
"@girs/shell-14": "14.0.0-4.0.0-beta.5",
"@girs/soup-3.0": "3.4.4-4.0.0-beta.5",
"@girs/st-14": "14.0.0-4.0.0-beta.5",
"@rollup/plugin-commonjs": "^25.0.8",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"@tsconfig/strictest": "^2.0.5",
"@types/events": "^3.0.3",
"@types/gettext-parser": "^4.0.4",
"@types/highlight.js": "^10.1.0",
"@types/is-url": "^1.2.32",
"@types/prismjs": "^1.26.3",
"@typescript-eslint/eslint-plugin": "^7.7.0",
"@typescript-eslint/parser": "^7.7.0",
"@types/jasmine": "^5.1.4",
"@types/prismjs": "^1.26.4",
"@typescript-eslint/eslint-plugin": "^7.12.0",
"@typescript-eslint/parser": "^7.12.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
Expand All @@ -71,16 +73,15 @@
"fill-pot-po": "^3.0.4",
"gettext-extractor": "^3.8.0",
"gettext-parser": "^7.0.1",
"glob": "^10.3.12",
"glob": "^10.4.1",
"husky": "^9.0.11",
"prettier": "^3.2.5",
"rollup": "^4.14.3",
"prettier": "^3.3.0",
"rollup": "^4.18.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-copy": "^3.5.0",
"rollup-plugin-styles": "^4.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"@tsconfig/strictest": "^2.0.5"
"typescript": "^5.4.5"
},
"dependencies": {
"date-fns": "^2.30.0",
Expand Down
35 changes: 35 additions & 0 deletions rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,40 @@ const thirdPartyBuild = thirdParty.map((pkg) => {
};
});

const testFiles = ['db.test'];

const testBuilds = testFiles.map((file) => {
return {
input: `tests/${file}.ts`,
treeshake: {
moduleSideEffects: 'no-external',
},
output: {
file: `build/tests/${file}.js`,
format: 'esm',
name: 'init',
paths: { ...ExtensionEntries, ...GlobalEntries },
assetFileNames: '[name][extname]',
generatedCode: {
constBindings: true,
},
},
external: [...gjsModules, ...globalDefinitionImports],
plugins: [
commonjs(),
nodeResolve({
preferBuiltins: false,
}),
typescript({
tsconfig: './tsconfig.json',
}),
cleanup({
comments: 'none',
}),
],
};
});

const builds = [
...thirdPartyBuild,
{
Expand Down Expand Up @@ -209,6 +243,7 @@ const builds = [
}),
],
},
...testBuilds,
];

export default builds;
3 changes: 2 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
debounceIds,
deleteAppDirs,
getCurrentExtensionSettings,
getDbPath,
loadInterfaceXML,
logger,
removeSoundContext,
Expand Down Expand Up @@ -134,7 +135,7 @@ export default class PanoExtension extends Extension {

private setupResources() {
setupAppDirs(this);
db.setup(this);
db.setup(getDbPath(this));
}

private async clearHistory() {
Expand Down
35 changes: 34 additions & 1 deletion src/utils/compatibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import GLib from '@girs/glib-2.0';
import { Notification, Source as MessageTraySource } from '@girs/gnome-shell/dist/ui/messageTray';
import St from '@girs/st-14';

import { logger } from './shell';

const debug = logger('compatibility');

// better typed functions for GDA

// we get / have to store strings for dates and numbers for boolean
Expand Down Expand Up @@ -39,6 +43,35 @@ export function add_expr_value(builder: Gda5.SqlBuilder | Gda6.SqlBuilder | SqlB
return builder.add_expr_value(null, value);
}

/**
* a faster unescape function for gda
*
* Does not the exact reverse of gda_default_escape_string(): that transforms any "''" into "'", we don't do that,
* since this is incorrect in our usage, just unescape any "\\" into "\" and any "\'" into "'".
* @param input string to unescape
* @returns unescaped string or the input, if an error was be found or nothing needs to be unescaped
*/
export function unescape_string(input: string): string {
// check if we need to escape something, so we don't mutate strings unnecessary, this speeds things up
if (!input.includes('\\')) {
return input;
}

try {
return input.replaceAll(/\\(.)/g, (_all, captured) => {
if (captured === '\\' || captured === "'") {
return captured;
}

throw new Error(`Unexpected escape character '${captured}'`);
});
} catch (error) {
debug(`Error in unescape: ${error}`);
// return the original string
return input;
}
}

// compatibility functions for gnome-shell 45 / 46

function isGnome45Notifications(): boolean {
Expand All @@ -50,7 +83,7 @@ export function newNotification(
text: string,
banner: string,
transient_: boolean,
params: Notification.Params,
params: Notification.ConstructorProps,
): Notification {
if (isGnome45Notifications()) {
// @ts-expect-error gnome 45 type
Expand Down
29 changes: 15 additions & 14 deletions src/utils/db.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Gda5 from '@girs/gda-5.0';
import type { ExtensionBase } from '@girs/gnome-shell/dist/extensions/sharedInternals';
import { add_expr_value, type DataModelIter, type SqlBuilder } from '@pano/utils/compatibility';
import { getDbPath, logger } from '@pano/utils/shell';
import { add_expr_value, type DataModelIter, type SqlBuilder, unescape_string } from '@pano/utils/compatibility';
import { logger } from '@pano/utils/shell';

const debug = logger('database');

Expand All @@ -18,6 +17,8 @@ export type DBItem = {
metaData?: string | undefined;
};

export type SaveDBItem = Omit<DBItem, 'id'>;

class ClipboardQuery {
readonly statement: Gda5.Statement;

Expand Down Expand Up @@ -172,16 +173,16 @@ export class ClipboardQueryBuilder {
class Database {
private connection: Gda5.Connection | null = null;

private init(ext: ExtensionBase) {
private init(dbPath: string) {
this.connection = new Gda5.Connection({
provider: Gda5.Config.get_provider('SQLite'),
cncString: `DB_DIR=${getDbPath(ext)};DB_NAME=pano`,
cncString: `DB_DIR=${dbPath};DB_NAME=pano`,
});
this.connection.open();
}

setup(ext: ExtensionBase) {
this.init(ext);
setup(dbPath: string) {
this.init(dbPath);
if (!this.connection || !this.connection.is_opened()) {
debug('connection is not opened');
return;
Expand All @@ -206,7 +207,7 @@ class Database {
`);
}

save(dbItem: Omit<DBItem, 'id'>): DBItem | null {
save(dbItem: SaveDBItem): DBItem | null {
if (!this.connection || !this.connection.is_opened()) {
debug('connection is not opened');
return null;
Expand Down Expand Up @@ -321,13 +322,13 @@ class Database {
const id = iter.get_value_for_field('id');
const itemType = iter.get_value_for_field('itemType');
const content = iter.get_value_for_field('content');
const contentUnescaped = Gda5.default_unescape_string(content) ?? content;
const contentUnescaped = unescape_string(content) ?? content;
const copyDate = iter.get_value_for_field('copyDate');
const isFavorite = iter.get_value_for_field('isFavorite');
const matchValue = iter.get_value_for_field('matchValue');
const matchValueUnescaped = Gda5.default_unescape_string(matchValue) ?? matchValue;
const matchValueUnescaped = unescape_string(matchValue) ?? matchValue;
const searchValue = iter.get_value_for_field('searchValue');
const searchValueUnescaped = searchValue ? Gda5.default_unescape_string(searchValue) ?? searchValue : undefined;
const searchValueUnescaped = searchValue ? unescape_string(searchValue) ?? searchValue : undefined;
const metaData = iter.get_value_for_field('metaData');

itemList.push({
Expand All @@ -345,9 +346,9 @@ class Database {
return itemList;
}

start(ext: ExtensionBase) {
if (!this.connection) {
this.init(ext);
start(dbPath: string) {
if (!this.connection && dbPath) {
this.init(dbPath);
}

if (this.connection && !this.connection.is_opened()) {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const logger =

const debug = logger('shell-utils');

const deleteFile = (file: Gio.File) => {
export const deleteFile = (file: Gio.File) => {
return new Promise((resolve, reject) => {
file.delete_async(GLib.PRIORITY_DEFAULT, null, (_file, res) => {
try {
Expand All @@ -22,7 +22,7 @@ const deleteFile = (file: Gio.File) => {
});
};

const deleteDirectory = async (file: Gio.File) => {
export const deleteDirectory = async (file: Gio.File) => {
try {
const iter: Gio.FileEnumerator | undefined = await new Promise((resolve, reject) => {
file.enumerate_children_async(
Expand Down
Loading