Skip to content
Merged
51 changes: 41 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A plugin that enables quick access to recent projects across various IDEs like V

Download from the store

## Supported applications
## Supported programs

- [x] visual studio code
- [x] pycharm
Expand All @@ -20,9 +20,9 @@ Download from the store
- [x] vscode ssh
- [x] typora

You can access different IDEs using the following format:
You can access different programs using the following acronyms:

```python
```json
{
"vsc": "VSCODE",
"py": "PYCHARM",
Expand All @@ -44,14 +44,14 @@ All configurations can be done in the Settings Panel. Change the configurations

### Program Path

- Configure application paths in the following format:
- Configure program paths in the following format:

```plaintext
APP_DOWNLOAD=C:/path/to/app.exe
APP_STORAGE=C:/path/to/storage/file
```

- Each application needs both DOWNLOAD and STORAGE entries
- Each program needs both DOWNLOAD and STORAGE entries
- Example configuration:

```plaintext
Expand All @@ -71,6 +71,37 @@ All configurations can be done in the Settings Panel. Change the configurations
TYPORA_STORAGE=C:/Users/YourUsername/AppData/Roaming/Typora/history.data
```

### Suggestions List

- You can customize the program suggestions list displayed when no input is provided.
- Configure program suggestions list in the following format:

```plaintext
VSCODE
PYCHARM
INTELLIJ_IDEA
# ... other programs
```

- Keep empty to display the default program suggestions list — the plugin will suggest the programs that are configured in the [Program Path](#program-path).

### Plugin Trigger Keyword

- Make sure to update this configuration when you change the trigger keyword of this plugin.
- It affects the usage of [Suggestions List](#suggestions-list).

### Custom Acronyms Map

- Configure custom acronyms for your programs in the following format:

```plaintext
VSCODE=vs
PYCHARM=pc
# ... other acronyms map
```

- This allows you to use custom acronyms when the [default acronyms](#supported-programs) do not match your preferences.

### Tips

- **Verify Paths**: Ensure that the paths you enter are correct and that the files or executables exist at those locations
Expand All @@ -81,23 +112,23 @@ All configurations can be done in the Settings Panel. Change the configurations

To open a project named "MyProject" in Visual Studio Code, you would use:

r
`r`

![1752128883045](image/README/1752128883045.png)

r vsc
`r vsc`

![1733284352742](image/README/1733284352742.png)

r vsc My
`r vsc My`

![1733284374591](image/README/1733284374591.png)

r vsc 空
`r vsc 空`

![1733284760505](image/README/1733284760505.png)

context menu
`context menu`

![1742873448581](image/README/1742873448581.png)

Expand Down
41 changes: 41 additions & 0 deletions SettingsTemplate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,44 @@ body:

Each application needs both DOWNLOAD and STORAGE entries.
defaultValue: ""
- type: textarea
attributes:
name: suggestions_list
label: Suggestions List
description: >
Configure list of program suggestions in the following format:

VSCODE

PYCHARM

INTELLIJ_IDEA

It will be displayed when the plugin is activated.

Keep empty to display the default program suggestions.
- type: input
- type: input
attributes:
name: plugin_trigger_keyword
label: Plugin Trigger Keyword
description: >
The keyword to trigger the plugin.

If you change it, please update here as well.
defaultValue: "r"
- type: textarea
attributes:
name: custom_acronyms_map
label: Custom Acronyms Map
description: >
Configure custom acronyms for your programs in the following format:

VSCODE=vsc

PYCHARM=py

Each line defines a mapping from the program name to its acronyms.
defaultValue: ""


File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
Binary file added icons/TYPORA.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
Binary file removed icons/ty_icon.png
Binary file not shown.
60 changes: 50 additions & 10 deletions src/core/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from click import UsageError

from .jsonrpc import settings
from .logger import get_logger

logger = get_logger()


class Config(dict):
Expand All @@ -10,10 +11,16 @@ def __init__(self):

def _parse_settings(self) -> None:
"""解析从 Flow Launcher 获取的配置"""
flow_settings = settings() # 直接从 jsonrpc 获取
flow_settings = settings()
if not flow_settings:
return
program_path = flow_settings.get("program_path", "")

# 解析 program_path 配置
program_path = (
flow_settings.get("program_path")
if "program_path" in flow_settings
else None
)
if program_path:
# 解析多行配置,格式如: KEY=VALUE\nKEY2=VALUE2
for line in program_path.split("\n"):
Expand All @@ -22,12 +29,45 @@ def _parse_settings(self) -> None:
key, value = line.split("=", 1)
self[key.strip()] = value.strip()

def get(self, key: str) -> str:
"""获取配置值,如果不存在则抛出异常"""
value = super().get(key)
if not value:
raise UsageError(f"Missing config key: {key}")
return value
# 解析 suggestions_list 配置(每行一个程序名称)
suggestions_list = (
flow_settings.get("suggestions_list")
if "suggestions_list" in flow_settings
else None
)
if suggestions_list:
self["suggestions_list"] = [
s.strip().upper() for s in suggestions_list.split("\n") if s.strip()
]

# 解析 acronyms_map 配置
custom_acronyms_map = (
flow_settings.get("custom_acronyms_map")
if "custom_acronyms_map" in flow_settings
else None
)
if custom_acronyms_map:
self["custom_acronyms_map"] = {}
for line in custom_acronyms_map.split("\n"):
line = line.strip()
if line and "=" in line:
program, acronyms = line.split("=", 1)
program = program.strip().upper()
acronyms = acronyms.strip()
self["custom_acronyms_map"][program] = acronyms

# 解析其他配置
for key, value in flow_settings.items():
if key not in self and key not in (
"suggestions_list",
"program_path",
"custom_acronyms_map",
):
self[key] = value

def get(self, key: str, default=None) -> str:
"""获取配置值,支持默认值参数"""
return super().get(key, default)


config = Config()
93 changes: 55 additions & 38 deletions src/core/factory.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from abc import ABC, abstractmethod
from typing import Dict, List
from typing import Dict

from .registry import ApplicationRegistry
from .logger import get_logger

logger = get_logger()


class AbstractFactory(ABC):
Expand Down Expand Up @@ -32,50 +35,64 @@ def get_supported_applications(cls):
return list(ApplicationRegistry.get_all_applications().keys())

@classmethod
def get_application_acronyms(cls) -> Dict[str, str]:
def get_application_acronyms(cls, plugin_config) -> Dict[str, str]:
"""
获取应用的缩写字典
获取应用的缩写字典,优先使用 plugin_config 中用户自定义的 custom_acronyms_map。
返回格式:
{
"vsc": "VSCODE",
"idea": "IDEA",
"pycharm": "PYCHARM"
"cs": "CURSOR",
...
}
"""
ApplicationRegistry.load_applications()
return ApplicationRegistry.get_acronyms_map()
default_acronyms_map = ApplicationRegistry.get_acronyms_map()
custom_acronyms_map = plugin_config.get("custom_acronyms_map", {})
acronyms_map = {acr: prog.upper() for prog, acr in custom_acronyms_map.items()}
for acr, prog in default_acronyms_map.items():
if prog not in acronyms_map.values():
acronyms_map[acr] = prog
return acronyms_map

@classmethod
def get_application_message(cls) -> List[Dict[str, str]]:
"""获得所有applications的消息列表
Returns:
[
{
"title": "VSCODE",
"subTitle": "vsc",
"icoPath": "icons/app.png",
"jsonRPCAction": {
"method": "Flow.Launcher.ChangeQuery",
"parameters": ["r vsc", False],
"dontHideAfterAction": True,
},
"score": 0,
},
]
def get_application_message(cls, plugin_config):
"""
根据配置的 suggestions_list 生成 Flow Launcher 消息列表。
如果未配置,则只展示用户已配置路径的应用。
"""
application_dict = ConcreteFactory.get_application_acronyms()
keys = list(application_dict.keys())
return [
{
"title": application_dict[keys[i]],
"subTitle": keys[i],
"icoPath": f"icons/{keys[i]}_icon.png",
"jsonRPCAction": {
"method": "Flow.Launcher.ChangeQuery",
"parameters": [f"r {keys[i]}", False],
"dontHideAfterAction": True,
},
"score": 0,
}
for i in range(len(keys))
]
suggestions_list = plugin_config.get("suggestions_list", None)
plugin_trigger_keyword = plugin_config.get("plugin_trigger_keyword", "r")
acronyms_dict = cls.get_application_acronyms(plugin_config)
reverse_acronyms_dict = {v: k for k, v in acronyms_dict.items()}

# 只显示已配置的应用建议
if suggestions_list is None:
suggestions_list = []
for _, program_name in acronyms_dict.items():
download_key = program_name + "_DOWNLOAD"
storage_key = program_name + "_STORAGE"
if download_key in plugin_config and storage_key in plugin_config:
suggestions_list.append(program_name)

messages = []
for idx, program_name in enumerate(suggestions_list):
if program_name in reverse_acronyms_dict:
acronyms = reverse_acronyms_dict[program_name]
messages.append(
{
"title": program_name,
"subTitle": acronyms,
"icoPath": f"icons/{program_name}.png",
"jsonRPCAction": {
"method": "Flow.Launcher.ChangeQuery",
"parameters": [
f"{plugin_trigger_keyword} {acronyms} ",
False,
],
"dontHideAfterAction": True,
},
# 按原始顺序排序
"score": (100 - idx) * 10000,
}
)
return messages
12 changes: 6 additions & 6 deletions src/core/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ def __init__(self):

def query(self, param: str) -> List[Dict[str, str]]:
args = param.strip()
acronyms_dict = ConcreteFactory.get_application_acronyms()

# 遍历显示
# 如果没有输入参数,则根据 suggestions_list 展示建议列表
if len(args) == 0:
return ConcreteFactory.get_application_message()
return ConcreteFactory.get_application_message(config)

acronyms = args.split(" ")[0]
acronyms_dict = ConcreteFactory.get_application_acronyms(config)

if acronyms not in acronyms_dict.keys():
return MessageDTO.asWarnFlowMessage(
Expand All @@ -35,8 +36,7 @@ def query(self, param: str) -> List[Dict[str, str]]:
)
else:
app_name = acronyms_dict[acronyms]
logger.debug(f"app_name: {app_name}")
icon_path = "icons/{}_icon.png".format(acronyms)
icon_path = f"icons/{app_name}.png"
query = "".join(args.split(" ")[1:])

# 读取配置
Expand All @@ -48,7 +48,7 @@ def query(self, param: str) -> List[Dict[str, str]]:
"{0} app_download or app_storage is None".format(app_name) + str(e),
"Please check your settings",
)
logger.debug(f"app_download: {app_download}, app_storage: {app_storage}")

# 读取recent_projects
try:
app = ConcreteFactory.create_app(app_name, app_download, app_storage)
Expand Down
Loading