You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Long list format
This commit is contained in:
		| @@ -5,6 +5,8 @@ import { Folder } from 'lib/models/folder.js'; | ||||
| import { Note } from 'lib/models/note.js'; | ||||
| import { autocompleteFolders } from './autocomplete.js'; | ||||
| import { sprintf } from 'sprintf-js'; | ||||
| import { time } from 'lib/time-utils.js'; | ||||
| import { vorpalUtils } from './vorpal-utils.js'; | ||||
|  | ||||
| class Command extends BaseCommand { | ||||
|  | ||||
| @@ -23,6 +25,7 @@ class Command extends BaseCommand { | ||||
| 			['-r, --reverse', 'Reverses the sorting order.'], | ||||
| 			['-t, --type <type>', 'Displays only the items of the specific type(s). Can be `n` for notes, `t` for todos, or `nt` for notes and todos (eg. `-tt` would display only the todos, while `-ttd` would display notes and todos.'], | ||||
| 			['-f, --format <format>', 'Either "text" or "json"'], | ||||
| 			['-l, --long', 'Use long list format. Format is NOTE_COUNT (for notebook), DATE, NEED_SYNC, TODO_CHECKED (for todos), TITLE'], | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| @@ -51,34 +54,65 @@ class Command extends BaseCommand { | ||||
| 		} | ||||
| 		if (pattern) queryOptions.titlePattern = pattern; | ||||
|  | ||||
| 		let modelType = null; | ||||
| 		if (pattern == '/' || !app().currentFolder()) { | ||||
| 			items = await Folder.all(queryOptions); | ||||
| 			suffix = '/'; | ||||
| 			modelType = Folder.modelType(); | ||||
| 		} else { | ||||
| 			if (!app().currentFolder()) throw new Error(_('Please select a notebook first.')); | ||||
| 			items = await Note.previews(app().currentFolder().id, queryOptions); | ||||
| 			modelType = Note.modelType(); | ||||
| 		} | ||||
|  | ||||
| 		if (options.format && options.format == 'json') { | ||||
| 			this.log(JSON.stringify(items)); | ||||
| 		} else { | ||||
| 			let seenTitles = []; | ||||
| 			let hasTodos = false; | ||||
| 			for (let i = 0; i < items.length; i++) { | ||||
| 				let item = items[i]; | ||||
| 				let line = ''; | ||||
| 				if (!!item.is_todo) { | ||||
| 					line += sprintf('[%s] ', !!item.todo_completed ? 'X' : ' '); | ||||
| 				if (item.is_todo) { | ||||
| 					hasTodos = true; | ||||
| 					break; | ||||
| 				} | ||||
| 				line += item.title + suffix; | ||||
| 			} | ||||
|  | ||||
| 			let seenTitles = []; | ||||
| 			let rows = []; | ||||
| 			for (let i = 0; i < items.length; i++) { | ||||
| 				let item = items[i]; | ||||
| 				let row = []; | ||||
|  | ||||
| 				if (options.long) { | ||||
| 					if (modelType == Folder.modelType()) { | ||||
| 						row.push(await Folder.noteCount(item.id)); | ||||
| 					} | ||||
|  | ||||
| 					row.push(time.unixMsToLocalDateTime(item.updated_time)); | ||||
| 					row.push(item.updated_time > item.sync_time ? '*' : ' '); | ||||
| 				} | ||||
|  | ||||
| 				let title = item.title + suffix; | ||||
| 				if (seenTitles.indexOf(item.title) >= 0) { | ||||
| 					line += ' (' + item.id.substr(0,4) + ')'; | ||||
| 					title += ' (' + item.id.substr(0,4) + ')'; | ||||
| 				} else { | ||||
| 					seenTitles.push(item.title); | ||||
| 				} | ||||
|  | ||||
| 				this.log(line); | ||||
| 				if (hasTodos) { | ||||
| 					if (item.is_todo) { | ||||
| 						row.push(sprintf('[%s]', !!item.todo_completed ? 'X' : ' ')); | ||||
| 					} else { | ||||
| 						row.push('   '); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				row.push(title); | ||||
|  | ||||
| 				rows.push(row); | ||||
| 			} | ||||
|  | ||||
| 			vorpalUtils.printArray(this, rows); | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|   | ||||
| @@ -8,6 +8,8 @@ import { Tag } from 'lib/models/tag.js'; | ||||
| import { Resource } from 'lib/models/resource.js'; | ||||
| import { Folder } from 'lib/models/folder.js'; | ||||
| import { enexXmlToMd } from './import-enex-md-gen.js'; | ||||
| import { time } from 'lib/time-utils.js'; | ||||
| import Levenshtein from 'levenshtein'; | ||||
| import jsSHA from "jssha"; | ||||
|  | ||||
| const Promise = require('promise'); | ||||
| @@ -50,9 +52,36 @@ function createNoteId(note) { | ||||
| 	return hash.substr(0, 32); | ||||
| } | ||||
|  | ||||
| function levenshteinPercent(s1, s2) { | ||||
| 	let l = new Levenshtein(s1, s2); | ||||
| 	if (!s1.length || !s2.length) return 1; | ||||
| 	return Math.abs(l.distance / s1.length); | ||||
| } | ||||
|  | ||||
| async function fuzzyMatch(note) { | ||||
| 	let notes = await Note.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 0 AND created_time = ? AND title = ?', [note.created_time, note.title]); | ||||
| 	return notes.length !== 1 ? null : notes[0]; | ||||
| 	if (note.created_time < time.unixMs() - 1000 * 60 * 60 * 24 * 360) { | ||||
| 		let notes = await Note.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 0 AND created_time = ? AND title = ?', [note.created_time, note.title]); | ||||
| 		return notes.length !== 1 ? null : notes[0]; | ||||
| 	} | ||||
|  | ||||
| 	let notes = await Note.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 0 AND created_time = ?', [note.created_time]); | ||||
| 	if (notes.length === 0) return null; | ||||
| 	if (notes.length === 1) return notes[0]; | ||||
|  | ||||
| 	let lowestL = 1; | ||||
| 	let lowestN = null; | ||||
| 	for (let i = 0; i < notes.length; i++) { | ||||
| 		let n = notes[i]; | ||||
| 		let l = levenshteinPercent(note.title, n.title); | ||||
| 		if (l < lowestL) { | ||||
| 			lowestL = l; | ||||
| 			lowestN = n; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (lowestN && lowestL < 0.2) return lowestN; | ||||
|  | ||||
| 	return null; | ||||
| } | ||||
|  | ||||
| async function saveNoteResources(note) { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { time } from 'lib/time-utils.js'; | ||||
| import stringPadding from 'string-padding'; | ||||
|  | ||||
| const vorpalUtils = {}; | ||||
|  | ||||
| @@ -13,6 +14,40 @@ function initialize(vorpal) { | ||||
| 	vorpal_ = vorpal; | ||||
| } | ||||
|  | ||||
| function printArray(commandInstance, rows, headers = null) { | ||||
| 	if (!rows.length) return ''; | ||||
|  | ||||
| 	const ALIGN_LEFT = 0; | ||||
| 	const ALIGN_RIGHT = 1; | ||||
|  | ||||
| 	let colWidths = []; | ||||
| 	let colAligns = []; | ||||
|  | ||||
| 	for (let i = 0; i < rows.length; i++) { | ||||
| 		let row = rows[i]; | ||||
| 		 | ||||
| 		for (let j = 0; j < row.length; j++) { | ||||
| 			let item = row[j]; | ||||
| 			let width = item ? item.toString().length : 0; | ||||
| 			let align = typeof item == 'number' ? ALIGN_RIGHT : ALIGN_LEFT; | ||||
| 			if (!colWidths[j] || colWidths[j] < width) colWidths[j] = width; | ||||
| 			if (colAligns.length <= j) colAligns[j] = align; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	let lines = []; | ||||
| 	for (let row = 0; row < rows.length; row++) { | ||||
| 		let line = []; | ||||
| 		for (let col = 0; col < colWidths.length; col++) { | ||||
| 			let item = rows[row][col]; | ||||
| 			let width = colWidths[col]; | ||||
| 			let dir = colAligns[col] == ALIGN_LEFT ? stringPadding.RIGHT : stringPadding.LEFT; | ||||
| 			line.push(stringPadding(item, width, ' ', dir)); | ||||
| 		} | ||||
| 		commandInstance.log(line.join(' ')); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function redrawEnabled() { | ||||
| 	// // Always disabled for now - doesn't play well with command.cancel() | ||||
| 	// // function (it makes the whole app quit instead of just the  | ||||
| @@ -102,6 +137,7 @@ function cmdPromptConfirm(commandInstance, message) { | ||||
| } | ||||
|  | ||||
| vorpalUtils.initialize = initialize; | ||||
| vorpalUtils.printArray = printArray; | ||||
| vorpalUtils.redraw = redraw; | ||||
| vorpalUtils.redrawDone = redrawDone; | ||||
| vorpalUtils.setRedrawEnabled = setRedrawEnabled; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| { | ||||
|   "name": "joplin-cli", | ||||
|   "name": "joplin", | ||||
|   "description": "CLI client for Joplin", | ||||
|   "license": "MIT", | ||||
|   "repository": { | ||||
| @@ -7,7 +7,7 @@ | ||||
|     "url": "https://github.com/laurent22/joplin" | ||||
|   }, | ||||
|   "url": "git://github.com/laurent22/joplin.git", | ||||
|   "version": "0.8.36", | ||||
|   "version": "0.8.40", | ||||
|   "bin": { | ||||
|     "joplin": "./main_launcher.js" | ||||
|   }, | ||||
| @@ -18,6 +18,7 @@ | ||||
|     "form-data": "^2.1.4", | ||||
|     "fs-extra": "^3.0.1", | ||||
|     "jssha": "^2.3.0", | ||||
|     "levenshtein": "^1.0.5", | ||||
|     "lodash": "^4.17.4", | ||||
|     "mkdirp": "^0.5.1", | ||||
|     "moment": "^2.18.1", | ||||
| @@ -30,6 +31,7 @@ | ||||
|     "source-map-support": "^0.4.15", | ||||
|     "sprintf-js": "^1.1.1", | ||||
|     "sqlite3": "^3.1.8", | ||||
|     "string-padding": "^1.0.2", | ||||
|     "string-to-stream": "^1.1.0", | ||||
|     "tcp-port-used": "^0.1.2", | ||||
|     "uuid": "^3.0.1", | ||||
|   | ||||
| @@ -43,6 +43,11 @@ class Folder extends BaseItem { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	static async noteCount(parentId) { | ||||
| 		let r = await this.db().selectOne('SELECT count(*) as total FROM notes WHERE is_conflict = 0 AND parent_id = ?', [parentId]); | ||||
| 		return r ? r.total : 0; | ||||
| 	} | ||||
|  | ||||
| 	static async delete(folderId, options = null) { | ||||
| 		let folder = await Folder.load(folderId); | ||||
| 		if (!folder) throw new Error('Trying to delete non-existing notebook: ' + folderId); | ||||
|   | ||||
| @@ -47,7 +47,7 @@ class Note extends BaseItem { | ||||
| 	} | ||||
|  | ||||
| 	static previewFields() { | ||||
| 		return ['id', 'title', 'body', 'is_todo', 'todo_completed', 'parent_id', 'updated_time']; | ||||
| 		return ['id', 'title', 'body', 'is_todo', 'todo_completed', 'parent_id', 'updated_time', 'sync_time']; | ||||
| 	} | ||||
|  | ||||
| 	static previewFieldsSql() { | ||||
|   | ||||
| @@ -22,6 +22,10 @@ let time = { | ||||
| 		return moment.unix(ms / 1000).utc().format('YYYY-MM-DDTHH:mm:ss') + 'Z'; | ||||
| 	}, | ||||
|  | ||||
| 	unixMsToLocalDateTime(ms) { | ||||
| 		return moment.unix(ms / 1000).format('DD/MM/YYYY HH:mm'); | ||||
| 	}, | ||||
|  | ||||
| 	msleep(ms) { | ||||
| 		return new Promise((resolve, reject) => { | ||||
| 			setTimeout(() => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user