From 65cf6f0842d8d5933dc13b3767b1baf398d68cd5 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka <ueokande@i-beam.org> Date: Mon, 14 Jun 2021 23:14:51 +0900 Subject: Implement FindNextOperator --- src/background/operators/impls/FindNextOperator.ts | 94 ++++++++++++++++++++++ .../operators/impls/FindOperatorFactoryChain.ts | 49 +++++++++++ src/background/operators/impls/FindPrevOperator.ts | 20 +++++ .../operators/impls/OperatorFactoryImpl.ts | 3 + 4 files changed, 166 insertions(+) create mode 100644 src/background/operators/impls/FindNextOperator.ts create mode 100644 src/background/operators/impls/FindOperatorFactoryChain.ts create mode 100644 src/background/operators/impls/FindPrevOperator.ts (limited to 'src/background/operators/impls') diff --git a/src/background/operators/impls/FindNextOperator.ts b/src/background/operators/impls/FindNextOperator.ts new file mode 100644 index 0000000..241f71d --- /dev/null +++ b/src/background/operators/impls/FindNextOperator.ts @@ -0,0 +1,94 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import FindRepository from "../../repositories/FindRepository"; +import FindClient from "../../clients/FindClient"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; +import FramePresenter from "../../presenters/FramePresenter"; + +export default class FindNextOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly findRepository: FindRepository, + private readonly findClient: FindClient, + private readonly consoleClient: ConsoleClient, + private readonly framePresenter: FramePresenter + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + const tabId = tab?.id; + if (tabId == null) { + return; + } + + const state = await this.findRepository.getLocalState(tabId); + if (state) { + // Start to find the keyword from the current frame which last found on, + // and concat it to end of frame ids to perform a wrap-search + // + // ,- keyword should be in this frame + // | + // [100, 101, 0, 100] + // | + // `- continue from frame id 100 + // + const targetFrameIds = state.frameIds + .slice(state.framePos) + .concat( + state.frameIds.slice(0, state.framePos), + state.frameIds[state.framePos] + ); + + for (let i = 0; i < targetFrameIds.length; ++i) { + const found = await this.findClient.findNext( + tabId, + targetFrameIds[i], + state.keyword + ); + if (found) { + this.findRepository.setLocalState(tabId, { + ...state, + framePos: (i + state.framePos) % state.frameIds.length, // save current frame position or first + }); + return; + } + this.findClient.clearSelection(tabId, targetFrameIds[i]); + } + + // The keyword is gone. + this.consoleClient.showError( + tabId, + "Pattern not found: " + state.keyword + ); + return; + } + + const keyword = await this.findRepository.getGlobalKeyword(); + if (keyword) { + const frameIds = await this.framePresenter.getAllFrameIds(tabId); + for (const frameId of frameIds) { + await this.findClient.clearSelection(tabId, frameId); + } + + for (let framePos = 0; framePos < frameIds.length; ++framePos) { + const found = await this.findClient.findNext( + tabId, + frameIds[framePos], + keyword + ); + if (found) { + await this.findRepository.setLocalState(tabId, { + frameIds, + framePos, + keyword, + }); + await this.consoleClient.showInfo(tabId, "Pattern found: " + keyword); + return; + } + } + this.consoleClient.showError(tabId, "Pattern not found: " + keyword); + return; + } + await this.consoleClient.showError(tabId, "No previous search keywords"); + } +} diff --git a/src/background/operators/impls/FindOperatorFactoryChain.ts b/src/background/operators/impls/FindOperatorFactoryChain.ts new file mode 100644 index 0000000..b71f032 --- /dev/null +++ b/src/background/operators/impls/FindOperatorFactoryChain.ts @@ -0,0 +1,49 @@ +import { inject, injectable } from "tsyringe"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import TabPresenter from "../../presenters/TabPresenter"; +import * as operations from "../../../shared/operations"; +import FindNextOperator from "./FindNextOperator"; +import FindPrevOperator from "./FindPrevOperator"; +import FindRepository from "../../repositories/FindRepository"; +import FindClient from "../../clients/FindClient"; +import FramePresenter from "../../presenters/FramePresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +@injectable() +export default class FindOperatorFactoryChain implements OperatorFactoryChain { + constructor( + @inject("TabPresenter") + private readonly tabPresenter: TabPresenter, + @inject("FindRepository") + private readonly findRepository: FindRepository, + @inject("FindClient") + private readonly findClient: FindClient, + @inject("ConsoleClient") + private readonly consoleClient: ConsoleClient, + @inject("FramePresenter") + private readonly framePresenter: FramePresenter + ) {} + + create(op: operations.Operation): Operator | null { + switch (op.type) { + case operations.FIND_NEXT: + return new FindNextOperator( + this.tabPresenter, + this.findRepository, + this.findClient, + this.consoleClient, + this.framePresenter + ); + case operations.FIND_PREV: + return new FindPrevOperator( + this.tabPresenter, + this.findRepository, + this.findClient, + this.consoleClient, + this.framePresenter + ); + } + return null; + } +} diff --git a/src/background/operators/impls/FindPrevOperator.ts b/src/background/operators/impls/FindPrevOperator.ts new file mode 100644 index 0000000..28238f6 --- /dev/null +++ b/src/background/operators/impls/FindPrevOperator.ts @@ -0,0 +1,20 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import FindRepository from "../../repositories/FindRepository"; +import FindClient from "../../clients/FindClient"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; +import FramePresenter from "../../presenters/FramePresenter"; + +export default class FindPrevOperator implements Operator { + constructor( + _tabPresenter: TabPresenter, + _findRepository: FindRepository, + _findClient: FindClient, + _consoleClient: ConsoleClient, + _framePresenter: FramePresenter + ) {} + + async run(): Promise<void> { + throw new Error("not implemented"); + } +} diff --git a/src/background/operators/impls/OperatorFactoryImpl.ts b/src/background/operators/impls/OperatorFactoryImpl.ts index 34e7bb5..ce87491 100644 --- a/src/background/operators/impls/OperatorFactoryImpl.ts +++ b/src/background/operators/impls/OperatorFactoryImpl.ts @@ -8,6 +8,7 @@ import NavigateOperatorFactoryChain from "./NavigateOperatorFactoryChain"; import RepeatOperatorFactoryChain from "./RepeatOperatorFactoryChain"; import TabOperatorFactoryChain from "./TabOperatorFactoryChain"; import ZoomOperatorFactoryChain from "./ZoomOperatorFactoryChain"; +import FindOperatorFactoryChain from "./FindOperatorFactoryChain"; import * as operations from "../../../shared/operations"; @injectable() @@ -20,6 +21,7 @@ export class OperatorFactoryImpl implements OperatorFactory { navigateOperatorFactoryChain: NavigateOperatorFactoryChain, tabOperatorFactoryChain: TabOperatorFactoryChain, zoomOperatorFactoryChain: ZoomOperatorFactoryChain, + findOperatorFactoryChain: FindOperatorFactoryChain, @inject(delay(() => RepeatOperatorFactoryChain)) repeatOperatorFactoryChain: RepeatOperatorFactoryChain ) { @@ -30,6 +32,7 @@ export class OperatorFactoryImpl implements OperatorFactory { repeatOperatorFactoryChain, tabOperatorFactoryChain, zoomOperatorFactoryChain, + findOperatorFactoryChain, ]; } -- cgit v1.2.3 From 05a19373e90976b82c2bd81626aee27a79eb4b2d Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka <ueokande@i-beam.org> Date: Mon, 5 Jul 2021 21:31:58 +0900 Subject: Implement backwards find --- src/background/clients/FindClient.ts | 15 ++++ src/background/operators/impls/FindPrevOperator.ts | 86 ++++++++++++++++++++-- 2 files changed, 95 insertions(+), 6 deletions(-) (limited to 'src/background/operators/impls') diff --git a/src/background/clients/FindClient.ts b/src/background/clients/FindClient.ts index 863c5ad..b46b964 100644 --- a/src/background/clients/FindClient.ts +++ b/src/background/clients/FindClient.ts @@ -3,6 +3,8 @@ import * as messages from "../../shared/messages"; export default interface FindClient { findNext(tabId: number, frameId: number, keyword: string): Promise<boolean>; + findPrev(tabId: number, frameId: number, keyword: string): Promise<boolean>; + clearSelection(tabId: number, frameId: number): Promise<void>; } @@ -20,6 +22,19 @@ export class FindClientImpl implements FindClient { return found; } + async findPrev( + tabId: number, + frameId: number, + keyword: string + ): Promise<boolean> { + const found = (await browser.tabs.sendMessage( + tabId, + { type: messages.FIND_PREV, keyword }, + { frameId } + )) as boolean; + return found; + } + clearSelection(tabId: number, frameId: number): Promise<void> { return browser.tabs.sendMessage( tabId, diff --git a/src/background/operators/impls/FindPrevOperator.ts b/src/background/operators/impls/FindPrevOperator.ts index 28238f6..822c386 100644 --- a/src/background/operators/impls/FindPrevOperator.ts +++ b/src/background/operators/impls/FindPrevOperator.ts @@ -7,14 +7,88 @@ import FramePresenter from "../../presenters/FramePresenter"; export default class FindPrevOperator implements Operator { constructor( - _tabPresenter: TabPresenter, - _findRepository: FindRepository, - _findClient: FindClient, - _consoleClient: ConsoleClient, - _framePresenter: FramePresenter + private readonly tabPresenter: TabPresenter, + private readonly findRepository: FindRepository, + private readonly findClient: FindClient, + private readonly consoleClient: ConsoleClient, + private readonly framePresenter: FramePresenter ) {} async run(): Promise<void> { - throw new Error("not implemented"); + const tab = await this.tabPresenter.getCurrent(); + const tabId = tab?.id; + if (tabId == null) { + return; + } + + const state = await this.findRepository.getLocalState(tabId); + if (state) { + // Start to find the keyword from the current frame which last found on, + // and concat it to end of frame ids to perform a wrap-search + // + // ,- keyword should be in this frame + // | + // [100, 101, 0, 100] + // | + // `- continue from frame id 100 + // + const targetFrameIds = state.frameIds + .slice(state.framePos) + .concat( + state.frameIds.slice(0, state.framePos), + state.frameIds[state.framePos] + ); + + for (let i = targetFrameIds.length - 1; i >= 0; --i) { + const found = await this.findClient.findPrev( + tabId, + targetFrameIds[i], + state.keyword + ); + if (found) { + this.findRepository.setLocalState(tabId, { + ...state, + framePos: (i + state.framePos) % state.frameIds.length, // save current frame position or first + }); + return; + } + this.findClient.clearSelection(tabId, targetFrameIds[i]); + } + + // The keyword is gone. + this.consoleClient.showError( + tabId, + "Pattern not found: " + state.keyword + ); + return; + } + + const keyword = await this.findRepository.getGlobalKeyword(); + if (keyword) { + const frameIds = await this.framePresenter.getAllFrameIds(tabId); + for (const frameId of frameIds) { + await this.findClient.clearSelection(tabId, frameId); + } + + for (let framePos = frameIds.length - 1; framePos >= 0; --framePos) { + const found = await this.findClient.findPrev( + tabId, + frameIds[framePos], + keyword + ); + if (found) { + await this.findRepository.setLocalState(tabId, { + frameIds, + framePos, + keyword, + }); + await this.consoleClient.showInfo(tabId, "Pattern found: " + keyword); + return; + } + } + this.consoleClient.showError(tabId, "Pattern not found: " + keyword); + return; + } + await this.consoleClient.showError(tabId, "No previous search keywords"); } } -- cgit v1.2.3