diff --git a/src/becca/entities/note.js b/src/becca/entities/note.js index ae1b1df52..00140a7d1 100644 --- a/src/becca/entities/note.js +++ b/src/becca/entities/note.js @@ -1107,6 +1107,13 @@ class Note extends AbstractEntity { return notePaths; } + /** + * @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree + */ + isHiddenCompletely() { + return !this.getAllNotePaths().find(notePathArr => !notePathArr.includes('_hidden')); + } + /** * @param ancestorNoteId * @return {boolean} - true if ancestorNoteId occurs in at least one of the note's paths diff --git a/src/public/app/entities/note_short.js b/src/public/app/entities/note_short.js index e496c6543..effd59790 100644 --- a/src/public/app/entities/note_short.js +++ b/src/public/app/entities/note_short.js @@ -364,6 +364,13 @@ class NoteShort { return notePaths; } + /** + * @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree + */ + isHiddenCompletely() { + return !this.getAllNotePaths().find(notePathArr => !notePathArr.includes('_hidden')); + } + __filterAttrs(attributes, type, name) { this.__validateTypeName(type, name); diff --git a/src/public/app/services/note_autocomplete.js b/src/public/app/services/note_autocomplete.js index 157c24261..07c75d447 100644 --- a/src/public/app/services/note_autocomplete.js +++ b/src/public/app/services/note_autocomplete.js @@ -230,6 +230,10 @@ function init() { $.fn.getSelectedNoteId = function () { const notePath = $(this).getSelectedNotePath(); + if (!notePath) { + return null; + } + const chunks = notePath.split('/'); return chunks.length >= 1 ? chunks[chunks.length - 1] : null; diff --git a/src/public/app/widgets/floating_buttons/code_buttons.js b/src/public/app/widgets/floating_buttons/code_buttons.js index 9363856a5..f1763870b 100644 --- a/src/public/app/widgets/floating_buttons/code_buttons.js +++ b/src/public/app/widgets/floating_buttons/code_buttons.js @@ -76,7 +76,7 @@ export default class CodeButtonsWidget extends NoteContextAwareWidget { this.$saveToNoteButton.toggle( note.mime === 'text/x-sqlite;schema=trilium' - && !note.getAllNotePaths().find(notePathArr => !notePathArr.includes('_hidden')) + && note.isHiddenCompletely() ); this.$openTriliumApiDocsButton.toggle(note.mime.startsWith('application/javascript;env=')); diff --git a/src/public/app/widgets/ribbon_widgets/search_definition.js b/src/public/app/widgets/ribbon_widgets/search_definition.js index 0dc4d4841..4ca8e17d9 100644 --- a/src/public/app/widgets/ribbon_widgets/search_definition.js +++ b/src/public/app/widgets/ribbon_widgets/search_definition.js @@ -270,7 +270,7 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget { async refreshWithNote(note) { this.$component.show(); - this.$saveToNoteButton.toggle(!note.getAllNotePaths().find(notePathArr => !notePathArr.includes('_hidden'))); + this.$saveToNoteButton.toggle(note.isHiddenCompletely()); this.$searchOptions.empty(); diff --git a/src/services/search/expressions/is_hidden.js b/src/services/search/expressions/is_hidden.js new file mode 100644 index 000000000..023c69087 --- /dev/null +++ b/src/services/search/expressions/is_hidden.js @@ -0,0 +1,23 @@ +"use strict"; + +const Expression = require('./expression'); +const NoteSet = require('../note_set'); + +/** + * Note is hidden when all its note paths start in hidden subtree (i.e. the note is not cloned into visible tree) + */ +class IsHiddenExp extends Expression { + execute(inputNoteSet, executionContext, searchContext) { + const resultNoteSet = new NoteSet(); + + for (const note of inputNoteSet.notes) { + if (note.isHiddenCompletely()) { + resultNoteSet.add(note); + } + } + + return resultNoteSet; + } +} + +module.exports = IsHiddenExp; diff --git a/src/services/search/search_context.js b/src/services/search/search_context.js index 6c427cf5f..95b8b8c57 100644 --- a/src/services/search/search_context.js +++ b/src/services/search/search_context.js @@ -6,10 +6,11 @@ class SearchContext { constructor(params = {}) { this.fastSearch = !!params.fastSearch; this.includeArchivedNotes = !!params.includeArchivedNotes; + this.includeHiddenNotes = !!params.includeHiddenNotes; this.ignoreHoistedNote = !!params.ignoreHoistedNote; this.ancestorNoteId = params.ancestorNoteId; - if (!this.ancestorNoteId && !this.ignoreHoistedNote && !hoistedNoteService.isHoistedInHiddenSubtree()) { + if (!this.ancestorNoteId && !this.ignoreHoistedNote) { // hoisting in hidden subtree should not limit autocomplete // since we want to link (create relations) to the normal non-hidden notes this.ancestorNoteId = hoistedNoteService.getHoistedNoteId(); diff --git a/src/services/search/services/parse.js b/src/services/search/services/parse.js index 0b121f959..6332f7728 100644 --- a/src/services/search/services/parse.js +++ b/src/services/search/services/parse.js @@ -18,7 +18,8 @@ const AncestorExp = require("../expressions/ancestor"); const buildComparator = require('./build_comparator'); const ValueExtractor = require('../value_extractor'); const utils = require("../../utils"); -const TrueExp = require("../expressions/true.js"); +const TrueExp = require("../expressions/true"); +const IsHiddenExp = require("../expressions/is_hidden"); function getFulltext(tokens, searchContext) { tokens = tokens.map(t => utils.removeDiacritic(t.token)); @@ -429,7 +430,7 @@ function parse({fulltextTokens, expressionTokens, searchContext}) { let exp = AndExp.of([ searchContext.includeArchivedNotes ? null : new PropertyComparisonExp(searchContext, "isarchived", "=", "false"), - (searchContext.ancestorNoteId && searchContext.ancestorNoteId !== 'root') ? new AncestorExp(searchContext.ancestorNoteId, searchContext.ancestorDepth) : null, + getAncestorExp(searchContext), getFulltext(fulltextTokens, searchContext), expression ]); @@ -448,4 +449,14 @@ function parse({fulltextTokens, expressionTokens, searchContext}) { return exp; } +function getAncestorExp({ancestorNoteId, ancestorDepth, includeHiddenNotes}) { + if (ancestorNoteId && ancestorNoteId !== 'root') { + return new AncestorExp(ancestorNoteId, ancestorDepth); + } else if (!includeHiddenNotes) { + return new NotExp(new IsHiddenExp()); + } else { + return null; + } +} + module.exports = parse; diff --git a/src/services/search/services/search.js b/src/services/search/services/search.js index 3e2f5af40..3b18c0b33 100644 --- a/src/services/search/services/search.js +++ b/src/services/search/services/search.js @@ -11,6 +11,7 @@ const beccaService = require('../../../becca/becca_service'); const utils = require('../../utils'); const log = require('../../log'); const scriptService = require("../../script"); +const hoistedNoteService = require("../../hoisted_note"); function searchFromNote(note) { let searchResultNoteIds, highlightedTokens; @@ -271,7 +272,11 @@ function searchNotesForAutocomplete(query) { const searchContext = new SearchContext({ fastSearch: true, includeArchivedNotes: false, - fuzzyAttributeSearch: true + includeHiddenNotes: true, + fuzzyAttributeSearch: true, + ancestorNoteId: hoistedNoteService.isHoistedInHiddenSubtree() + ? 'root' + : hoistedNoteService.getHoistedNoteId() }); const allSearchResults = findResultsWithQuery(query, searchContext);