diff --git a/SettingsTemplate.yaml b/SettingsTemplate.yaml new file mode 100644 index 0000000..e32d67b --- /dev/null +++ b/SettingsTemplate.yaml @@ -0,0 +1,6 @@ +body: + - type: textarea + attributes: + name: excluded_vault_paths + label: "Paths to be excluded from search." + description: "Exclude paths from being included in the search. One aboslute path per line." \ No newline at end of file diff --git a/plugin/main.py b/plugin/main.py index b0d1ea8..0a40d61 100644 --- a/plugin/main.py +++ b/plugin/main.py @@ -1,33 +1,53 @@ from difflib import SequenceMatcher as SM from flox import Flox +from pathlib import Path import obsidian -CHECK_BOX_GLYPH = '\ue003' -MARKED_CHECK_BOX_GLYPH = '\ue005' +CHECK_BOX_GLYPH = "\ue003" +MARKED_CHECK_BOX_GLYPH = "\ue005" def match(query, match): - return int(SM(lambda x: x == " ", query.lower().replace('\\', ' '), match.lower().replace('\\', ' '), autojunk=False).ratio() * 100) + return int( + SM( + lambda x: x == " ", + query.lower().replace("\\", " "), + match.lower().replace("\\", " "), + autojunk=False, + ).ratio() + * 100 + ) + class Obsidian(Flox): + def __init__(self): + super().__init__() + + lines = self.settings.get("excluded_vault_paths").split("\n") + paths = [Path(line.strip()) for line in lines if line.strip()] + + self.excluded_notes = set() + for p in paths: + self.excluded_notes.update(p for p in p.rglob("*.md")) def query(self, query): try: vaults = obsidian.get_vaults() except FileNotFoundError: self.add_item( - title='Obsidian not found', - subtitle='Please install Obsidian', + title="Obsidian not found", + subtitle="Please install Obsidian", ) return + for vault in vaults: - for note in vault.notes(): + for note in vault.notes(self.excluded_notes): title_score = match(query, note.title) subtitle_score = match(query, str(note.vault_path)) score = max(title_score, subtitle_score) - if score > 20 or query == '': + if score > 20 or query == "": self.add_item( title=note.title, subtitle=str(note.vault_path), @@ -35,7 +55,7 @@ def query(self, query): method=self.open_note, parameters=[vault.name, str(note.relative_path)], score=score, - context=[vault.id, str(note.path), note.checklists()] + context=[vault.id, str(note.path), note.checklists()], ) def context_menu(self, data): @@ -43,21 +63,21 @@ def context_menu(self, data): note_path = data[1] for checks in data[2]: self.add_item( - title=checks['description'], - subtitle=checks['title'], - glyph=MARKED_CHECK_BOX_GLYPH if checks['checked'] else CHECK_BOX_GLYPH, + title=checks["description"], + subtitle=checks["title"], + glyph=MARKED_CHECK_BOX_GLYPH if checks["checked"] else CHECK_BOX_GLYPH, method=self.toggle_checkbox, - parameters=[vault_id, note_path, checks['raw']], - dont_hide=True + parameters=[vault_id, note_path, checks["raw"]], + dont_hide=True, ) def toggle_checkbox(self, vault_id, note_path, raw): note = obsidian.get_note(vault_id, note_path) note.toggle_checkbox(raw) - def open_note(self, vault_name, note_path): obsidian.open_note(vault_name, note_path) + if __name__ == "__main__": Obsidian() diff --git a/plugin/obsidian.py b/plugin/obsidian.py index abef7c4..d4a3a37 100644 --- a/plugin/obsidian.py +++ b/plugin/obsidian.py @@ -6,86 +6,89 @@ logger = logging.getLogger(__name__) -VAULTS_FILE = 'obsidian.json' -VAULTS_PATH = Path(os.getenv('APPDATA'), 'obsidian', VAULTS_FILE) -CHECK_BOX = '- [ ]' -MARKED_CHECK_BOX = '- [x]' +VAULTS_FILE = "obsidian.json" +VAULTS_PATH = Path(os.getenv("APPDATA"), "obsidian", VAULTS_FILE) +CHECK_BOX = "- [ ]" +MARKED_CHECK_BOX = "- [x]" + def get_vaults(): vaults = [] try: - with open(VAULTS_PATH, 'r', encoding='utf-8', errors='replace') as f: + with open(VAULTS_PATH, "r", encoding="utf-8", errors="replace") as f: data = json.load(f) except FileNotFoundError: - logger.error(f'{VAULTS_PATH} not found!\nIs obsidian installed?') + logger.error(f"{VAULTS_PATH} not found!\nIs obsidian installed?") raise else: - for vault in data['vaults'].keys(): - vaults.append(Vault(vault, data['vaults'][vault])) + for vault in data["vaults"].keys(): + vaults.append(Vault(vault, data["vaults"][vault])) return vaults + def get_vault(id): try: - with open(VAULTS_PATH, 'r', encoding='utf-8', errors='replace') as f: + with open(VAULTS_PATH, "r", encoding="utf-8", errors="replace") as f: data = json.load(f) except FileNotFoundError: - logger.error(f'{VAULTS_PATH} not found!\nIs obsidian installed?') + logger.error(f"{VAULTS_PATH} not found!\nIs obsidian installed?") raise else: try: - return Vault(id, data['vaults'][id]) + return Vault(id, data["vaults"][id]) except KeyError: - logger.error(f'{id} not found!') + logger.error(f"{id} not found!") raise + def get_note(vault_id, note_path): vault = get_vault(vault_id) return Note(vault, note_path) def open_note(vault_name, note_path): - URI = f'open?vault={vault_name}&file={note_path}'.replace(' ', '%20').replace('/', '%2F').replace('\\', '%2F') - URI = f'obsidian://{URI}' + URI = ( + f"open?vault={vault_name}&file={note_path}".replace(" ", "%20") + .replace("/", "%2F") + .replace("\\", "%2F") + ) + URI = f"obsidian://{URI}" webbrowser.open(URI) -class Vault(object): - def __init__(self, id:str, vault: dict): +class Vault(object): + def __init__(self, id: str, vault: dict): self._data = vault self.id = id - self.name = Path(vault['path']).name + self.name = Path(vault["path"]).name for key, value in vault.items(): setattr(self, key, value) - def notes(self): + def notes(self, excluded: set): notes = [] - for note in Path(self.path).glob('**/*.md'): - notes.append(Note(self, note)) - return notes - def note(self, note_path): - for note in self.notes(): - if str(note.relative_path) == note_path: - return note + for note in Path(self.path).glob("**/*.md"): + if note not in excluded: + notes.append(Note(self, note)) + return notes -class Note(object): +class Note(object): def __init__(self, vault: Vault, full_path: str): self.vault = vault self.path = full_path - self.title = Path(full_path).name.replace('.md', '') - self.relative_path = Path(str(full_path).replace(f'{self.vault.path}', '')) - self.vault_path = f'{self.vault.name}{self.relative_path}' + self.title = Path(full_path).name.replace(".md", "") + self.relative_path = Path(str(full_path).replace(f"{self.vault.path}", "")) + self.vault_path = f"{self.vault.name}{self.relative_path}" def open_note(self): open_note(self.vault.name, self.relative_path) def content(self): - with open(self.path, 'r', encoding='utf-8', errors='replace') as f: + with open(self.path, "r", encoding="utf-8", errors="replace") as f: return f.read() - def toggle_checkbox(self, raw): content = self.content() for line in content.splitlines(): @@ -96,42 +99,44 @@ def toggle_checkbox(self, raw): toggled_line = line.replace(CHECK_BOX, MARKED_CHECK_BOX) break content = content.replace(line, toggled_line) - with open(self.path, 'w', encoding='utf-8', errors='replace') as f: + with open(self.path, "w", encoding="utf-8", errors="replace") as f: f.write(content) def checklists(self): checklists = [] - title = '' - prev_line = '' + title = "" + prev_line = "" for line in self.content().splitlines(): if CHECK_BOX in line or MARKED_CHECK_BOX in line: - description = line.replace(CHECK_BOX, '').replace(MARKED_CHECK_BOX, '').strip() + description = ( + line.replace(CHECK_BOX, "").replace(MARKED_CHECK_BOX, "").strip() + ) if MARKED_CHECK_BOX in line: checked = True else: checked = False - if (CHECK_BOX not in prev_line and MARKED_CHECK_BOX not in prev_line) and prev_line.endswith(':'): - title = prev_line.replace(':', '').strip() + if ( + CHECK_BOX not in prev_line and MARKED_CHECK_BOX not in prev_line + ) and prev_line.endswith(":"): + title = prev_line.replace(":", "").strip() checklists.append( { - 'title': title, - 'description': description, - 'checked': checked, - 'raw': line + "title": title, + "description": description, + "checked": checked, + "raw": line, } ) else: - title = '' + title = "" prev_line = line return checklists - - if __name__ == "__main__": vaults = get_vaults() for vault in vaults: - for note in vault.notes(): + for note in vault.notes(set()): print(note.title) open_note(vault.name, str(note.relative_path)) - break \ No newline at end of file + break