1
0
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:
Laurent Cozic 2017-07-12 21:39:47 +01:00
parent b596747612
commit 74e112fef9
7 changed files with 122 additions and 12 deletions

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -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",

View File

@ -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);

View File

@ -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() {

View File

@ -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(() => {