mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Long list format
This commit is contained in:
parent
b596747612
commit
74e112fef9
@ -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(() => {
|
||||
|
Loading…
Reference in New Issue
Block a user