カット・ペーストを可能にして、ダイナミックな移動を可能に〜1Writerカスタマイズ②〜

Obsidianが日々の相棒となってくれており、iPhoneでは「1Writer」というアプリを使ってObsidianで扱っているファイルと同じファイルを編集できるようにしています。

1Writer – Markdown Text Editor
カテゴリ: 仕事効率化, ユーティリティ

そして、リストアプリのようにテキストを扱えるようにするにはなにが必要かを考え、

  • 行ごとの削除
  • 行ごとの移動

の2つが欠かせないということで、

にて、削除と上下の移動は可能にすることができました。が、もっとダイナミックに移動させたいという願望が出てきました。


リストアプリでは、長押しや「編集」ボタンを押すことで、項目のドラッグ&ドロップが可能になるものが多いと思います。で、これが結構大事でして。
タスクを、実行しようと思っている順に並べているのですが、実際はそれ通りにいくことはあんまりありません。時にはリストの項目を大きく移動させたい時が出てきます。そんな時に重宝するのがドラッグ&ドロップの機能なわけです。
でもこれをテキストエディタに求めるのは、つまり1行ごとにドラッグ&ドロップによる移動ができるようにするのは、なかなかに難しい。
でもやっぱり、大きく移動させる機能は欲しい。

ということで、「カット」と「ペースト」を気軽にできるようにすることで、ドラッグ&ドロップのような動きを可能にしよう、と考えました。

  • カーソル(選択)行をカット
  • クリップボードの内容をペースト

という2つの機能があれば、リストの項目を大きく動かしたい時は、動かしたい項目をカットして動かしてきたかった場所でペーストすることができるので、ドラッグ&ドロップ機能の代わりができるだろう、と。
実際に動きを見てみましょう。

移動させたい行にカーソルを持っていく。あるいは、複数行に渡る場合は範囲選択をしておき、カット。するとその行がカットされる。移動したい先にカーソルを持っていって、ペーストボタンを押せばそこに移動完了です。
この機能で、さらにテキストエディタの内容を1行ごとに操作することが、以前よりやりやすくなりました。

カーソル(選択)行をカット

var text = editor.getText();
var linetexts = text.split('\n');
const range = editor.getSelectedRange();
var s = range[0];
var e = range[1];
var selects;
var selecte;
var linecount = 0;
var selectrange;
for (let i = 0; i < linetexts.length; i++){
    if ( s <
        linecount + linetexts[i].length +1){
        selects = i;
        selectrange = linecount - 1;
        break;
    }else{
        linecount += linetexts[i].length + 1;
    }
}
linecount = 0;
for (let i = 0; i < linetexts.length; i++){
if ( e <
    linecount + linetexts[i].length +1){
        selecte = i;
        break;
    }else{
        linecount += linetexts[i].length + 1;
    }
}

var before = -1;
for(let i=0; i < selects;i++){
    before = before + linetexts[i].length + 1;
}
var after = before;
if(before < 0){
    before = 0;
}
for(let i=selects; i < selecte+1;i++){
    after = after + linetexts[i].length + 1;
}
editor.replaceTextInRange(before, after, "")
var cutlines = [];
for(let i = 0; i < selecte-selects+1; i++){
    cutlines.push(linetexts[selects + i])
}
var cuttext = cutlines.join('\n');
app.setClipboard('\n' + cuttext);

クリップボードの内容をペースト

“`
var text = editor.getText();
var linetexts = text.split('\n');
const range = editor.getSelectedRange();
var s = range[0];
var e = range[1];

var cuttext = app.getClipboard();

editor.replaceTextInRange(s,e,cuttext);
“`

テキストエディタ上のテキストを1行ごとに扱うことができるようになることが、iPhone上ではこんなに快適になるものなんだな、と驚いています。
ついでに、カーソル行をコピーするアクションも作りました。

カーソル(選択)行をコピー

var text = editor.getText();
var linetexts = text.split('\n');
const range = editor.getSelectedRange();
var s = range[0];
var e = range[1];
var selects;
var selecte;
var linecount = 0;
var selectrange;
for (let i = 0; i < linetexts.length; i++){
if ( s <
linecount + linetexts[i].length +1){
selects = i;
selectrange = linecount - 1;
break;
}else{
linecount += linetexts[i].length + 1;
}
}
linecount = 0;
for (let i = 0; i < linetexts.length; i++){
if ( e <
linecount + linetexts[i].length +1){
selecte = i;
break;
}else{
linecount += linetexts[i].length + 1;
}
}
var cutlines = [];
for(let i = 0; i < selecte-selects+1; i++){
cutlines.push(linetexts[selects + i])
}
var cuttext = cutlines.join('\n');
app.setClipboard('\n' + cuttext);

  • カーソル(選択)行を削除
  • カーソル(選択)行を上へ移動
  • カーソル(選択)行を下へ移動
  • カーソル(選択)行をカット
  • カーソル(選択)行をコピー
  • クリップボードの内容をペースト

以上のアクションをカスタムキーボードに設定し、毎日使っています。

とても快適になったわけですが、1Writerで、たすくまのように「全ての行動を記録する」となると、行ごとの操作に加え、リピートタスクを設定し、それを簡単に呼び出せる仕組みが必要になってきます。全ての行動を記録する際、毎日やっていることに関しては、。毎日入力するよりもテンプレートで引っ張って来る方が明らかに手間が省けます。ましてや、たすくまのタスク数は大体40程度。それらを毎日入力するのは現実的ではありません。

そこで、1Writerにも、テンプレートを引っ張ってきてエディタ部分に入力し、たくさんのタスク名をいちいち入力しなくて住むようになりました。が、これはまた別の機会に。

テキストを「1行ごとのリスト」ととらえ、削除と移動を可能に〜1Writerカスタマイズ①〜

タスク管理アプリの多くは、長年使っている「たすくま」もそうですが「リスト」の形式をとっています。

1タスク1リスト(1タスク1行)の表示で、タスクがダァーッと並んでいる形式。
確かにこの形式は、タスク管理をする上で扱いやすいなぁと感じます。
リスト形式であれば、

  • 行の削除ができる
  • 行の順番の入れ替えができる

という機能を備えており、それがタスクを扱いやすくしてくれている要因ではないか、ということで、「やったことの記録がすこぶる取りやすいリストアプリ」のように、テキストを扱えるようにしたいな、という思いがわいたわけでした。
「アウトライナー」をタスク管理に利用している方も多くいられると思うのですが、それは、アウトライナーは行の削除と順番の入れ替えが容易であるというのも大いに関係していると感じます。

ぼくは、タスク管理に今はObsidianを使っているのですが、iPhoneでは、「1Writer」というアプリを使っています。

1Writer – Markdown Text Editor
カテゴリ: 仕事効率化, ユーティリティ

1Writerはjavascriptを扱えますので、テキストを加工することができます。そこで、1Writerのjavascript機能を使って、

  • 行の削除ができる
  • 行の順番の入れ替えができる

という機能を実装しようと考えました。
そうして作成したのが、以下の3つのアクション。

  • カーソル(選択)行を削除
  • カーソル(選択)行を上へ移動
  • カーソル(選択)行を下へ移動

カーソル(選択)行を削除

var text = editor.getText();
var linetexts = text.split('\n');
const range = editor.getSelectedRange();
var s = range[0];
var e = range[1];
var selects;
var selecte;
var linecount = 0;
var selectrange;
for (let i = 0; i < linetexts.length; i++){
    if ( s <
        linecount + linetexts[i].length +1){
        selects = i;
        selectrange = linecount - 1;
        break;
    }else{
        linecount += linetexts[i].length + 1;
    }
}
linecount = 0;
for (let i = 0; i < linetexts.length; i++){
    if ( e <
        linecount + linetexts[i].length +1){
        selecte = i;
        break;
    }else{
        linecount += linetexts[i].length + 1;
    }
}

var before = -1;
for(let i=0; i < selects;i++){
    before = before + linetexts[i].length + 1;
}
var after = before;
if(before < 0){
    before = 0;
}
for(let i=selects; i < selecte+1;i++){
    after = after + linetexts[i].length + 1;
}
editor.replaceTextInRange(before, after, "")

カーソル(選択)行を上へ移動

var text = editor.getText();
var linetexts = text.split('\n');
const range = editor.getSelectedRange();
var s = range[0];
var e = range[1];
var selects;
var selecte;
var linecount = 0;
var selectrange;
for (let i = 0; i < linetexts.length; i++){
    if ( s <
        linecount + linetexts[i].length +1){
        selects = i;
        selectrange = linecount - 1;
    break;
    }else{
        linecount += linetexts[i].length + 1;
    }
}
linecount = 0;
for (let i = 0; i < linetexts.length; i++){
    if ( e <
        linecount + linetexts[i].length +1){
        selecte = i;
        break;
    }else{
        linecount += linetexts[i].length + 1;
    }
}

var before = 0;
for(let i=0; i < selects;i++){
    before = before + linetexts[i].length + 1;
}
var after = before;
if(before < 0){
    before = 0;
}
for(let i=selects; i < selecte+1;i++){
    after = after + linetexts[i].length + 1;
}
editor.replaceTextInRange(before, after, "")
var cutlines = [];
for(let i = 0; i < selecte-selects+1; i++){
    cutlines.push(linetexts[selects + i])
}
var cuttext = cutlines.join('\n');

if ( selects == 0 ){
    editor.replaceTextInRange(0, 0, cuttext + '\n');
    editor.setSelectedRange(cuttext.length,cuttext.length);
}else if( selects == 1 ){
    editor.replaceTextInRange(0, 0, cuttext + '\n');
    editor.setSelectedRange(s - linetexts[0].length-1,e-linetexts[0].length-1);
}else{
    var before = 0;
    for(let i=0; i < selects-1;i++){
        before = before + linetexts[i].length + 1;
    }
    editor.replaceTextInRange(before-1, before-1, '\n'+cuttext)
    editor.setSelectedRange(s - linetexts[selects-1].length-1,e-linetexts[selects-1].length-1);
}

カーソル(選択)行を下へ移動

var text = editor.getText();
var linetexts = text.split('\n');
const range = editor.getSelectedRange();
var s = range[0];
var e = range[1];
var selects;
var selecte;
var linecount = 0;
var selectrange;
for (let i = 0; i < linetexts.length; i++){
    if ( s <
        linecount + linetexts[i].length +1){
        selects = i;
        selectrange = linecount - 1;
        break;
    }else{
        linecount += linetexts[i].length + 1;
    }
}
linecount = 0;
for (let i = 0; i < linetexts.length; i++){
    if ( e <
        linecount + linetexts[i].length +1){
        selecte = i;
        break;
    }else{
        linecount += linetexts[i].length + 1;
    }
}

var before = 0;
for(let i=0; i < selects;i++){
    before = before + linetexts[i].length + 1;
}

if ( selecte == linetexts.length-1 ){

}else{

    var after = before;
    if(before < 0){
        before = 0;
    }
    for(let i=selects; i < selecte+1;i++){
        after = after + linetexts[i].length + 1;
    }
    editor.replaceTextInRange(before, after, "")
    var cutlines = [];
    for(let i = 0; i < selecte-selects+1; i++){
        cutlines.push(linetexts[selects + i])
    }
    var cuttext = cutlines.join('\n');
    var before = 0;
    for(let i=0; i < selecte+1;i++){
        before = before + linetexts[i].length + 1;
    }
    editor.replaceTextInRange(before-1+linetexts[selecte+1].length-cuttext.length, before-1+linetexts[selecte+1].length-cuttext.length, '\n'+cuttext);
    editor.setSelectedRange(s + linetexts[selecte+1].length+1,e+linetexts[selecte+1].length+1);
}

おそらくあんまりキレイなコードではないと思うのですが、、、便利なのでよかったら使ってみてください。

で、これらによって、テキストを行ごとに削除したり移動したりが簡単になったわけです。でも、だいぶと便利になったのに、さらに欲が出てきまして。
もっとダイナミックに移動させたいようになってきました。

つづく

自分で書いた記事・R-styleの記事ではっきり覚えているもの WRM「今週のQ」

倉下忠憲のWRM 「読む・書く・考えるの探求」第546号、ラディカルなRashitaのスタイルの変容の「今週のQ」での問いかけです。


ふと考えてみて、すぐに思いつくのは。「数学からぼくが学んだこと」と題しての連載です。

この4つのエントリで、自分が思う数学の良さや数学との向き合い方をけっこう伝えることができたのではないかと感じるくらいに、かなり力を入れた、じっくりと書き上げた記事となりました。

他には、

もとても思い入れの強い記事です。

は、かなり前に書いたものを、他の人が6年越しにツイートしてくれて、ふと目に飛び込んできて読み返して少しだけ修正を加えて新たにポストしたもの。

自分で書いた記事ではっきり覚えているものはこれらの記事かな、と思います。
どれも、数学に関して書いたもの。
数学についてどんどんブログで書きたい、と思っているものの、なかなかにそれができておらず、でもなんとかしっかりまとめ切れたという感覚があるからこそ、これらの記事はかなりはっきりと覚えているのだろうな、と思います。


R-styleの記事ではっきり覚えているのは、個人の書くブログの楽しさを教えてくれた

から始まる、一連の連載。
R-styleで展開される、Evernoteというツール越しに見る知的生産についての話が、すごく面白く、興味深く、ブログというのは面白いんだなーと思うようになったきっかけの記事となります。
で、そこから自分もブログを書きたい、となって実際に更新を始めるまでさほど時間はかかりませんでした。で、今もまた更新している、とうわけです。

他には、今ふと思い出したのが、

という、4月1日に書かれた記事。もう11年前になるんですね。
なぜこの記事が記憶に残っているかというと、

という本が、後に発売されることとなるからです。
書評 クラウド時代の知的生産の技術(倉下忠憲)が書かれたのは、倉下さんが物書きになる前、『Evernote「超」仕事術』の発売される前だったと思います。まだ本を1冊も世に出していない状況で、自分が本を出した体で記事がかかれているのがこの記事。
やがてコンビニの店長から物書きにジョブチェンジされて、『Evernote「超」仕事術』が発売され、『Evernote「超」知的生産術』がその後に続きます。
そして、『Evernoteとアナログノートによる ハイブリッド発想術』が発売され、その告知エントリが書かれて。
「ついに!!」と感動・興奮した記憶があるので、とても記憶に残っているのだと思います。

他にも、

は、よく覚えている記事の一つ。R-styleにて倉下さんの書く記事に触発されて自分も記事を書いたりしますが、やはりそういう触発を生んでくれた記事は覚えているものが多いように感じます。


自分のブログとR-styleの記事の中から覚えているものをあげてみました。
記憶力に自信がないく、基本的に忘れっぽい自分でも覚えている記事たちは、自分の中に色濃く残っているものたち、ということになると思います。
そういう記事をもっと書いていけたらなぁと思わなくもありませんが、ぼちぼちと日々書き続けるのを楽しくやってるので、まずは楽しむことを第一に今後も書き続けたいと思います。

では、お読みいただきありがとうございました。

抽象化の先に待っているものが感じられるTED動画

小学校で学ぶ算数と、中学校以降に学ぶ数学。このふたつには大きな違いがあります。
よく「算数は普段使うから必要やけど、数学を普段使うことはない。やから必要ない」って意見を聞いたりしますが、「必要ない」という部分以外はまさにそのとおりだと思います。「算数は普段使うから必要やけど、数学を普段使うことはない」。だって、扱う対象が違うんですもの。

算数で扱うのは、”具体的”です。具体的な数を扱い、数に関する知識を学びながら、四則演算を習熟していきます。に対して数学で扱う対象は”抽象的”なもの。数学では「文字式」を筆頭に、普段の生活ではなかなか出てこない、抽象化された世界へと踏み込んで行きます。
だから「算数は普段使うから必要やけど、数学を普段使うことはない」んです。ただ、必要ないことは決してないですが。

抽象化してなんなのさ

じゃあ、抽象化していったいなんだというのでしょうか。

中学校に入り、いきなり度肝を抜かれるのが、文字式だと思います。
それまでは500とか1.2とか具体的な数字を用いていたのに、なんだかxとかyとかが登場してきて、それで式を表現したりします。抽象化された世界への第一歩を踏み出すわけです。

算数の有名な問題で「鶴亀算」というのがあります。鶴と亀の数とその足の総数からそれぞれ何匹いるのかを求める問題です。

算数の世界ではあくまでも具体的な数を扱うので、まずは「すべて鶴であるとする」という仮定から始めての、総数を求めて行きます。それはそれで大切な考え方を学ぶことができるのですが、文字式を獲得したあとであれば、”方程式”という強力な武器が使えるようになります。こっちの方は鶴亀算のみならず、いろんな問題に応用可能です。

具体的な世界を離れる、ということは、応用範囲を広めることにつながります。

「ロバートラングが全く新しい時代の折り紙を折る」

抽象化することの恩恵がすごく感じられる動画が、TEDにあります。それが「ロバートラングが全く新しい時代の折り紙を折る」。

とにもかくにも動画を見て欲しいなと思います。「折り紙もすごいとこまできたもんだ」と感心すること請け合いです。

ロバート・ラングが全く新しい時代の折り紙を折る | Video on TED.com

今回注目して欲しいのは、折り方をコンピュータに計算してもらうことができるようになるまでの話の部分です。
そこでは、いろんな折り方のすべてに共通する要素を抜き出してしまうと、たった4つの法則にたどり着くことができる、ということが述べられています。
折り紙には古くから培われてきた、本当に様々な折り方ってのが存在します。これまでは一つ一つ折り方を編み出してきたわけですが、すべての折り方に共通する法則を抜き出すことで、折り紙の世界はとたんに広がります。
簡単な骨組みを考えることで折り方をコンピュータにはじき出してもらうことができるようになったんですから。
具体物の一つ一つをから構成されていた折り紙の世界を、すべての折り方に共通している要素、”4つの法則”を抜き出すことで抽象化し、単純な要素のみで表現することで、しまいにはコンピュータに計算してもらえるようにしちゃったわけです。
コンピュータの技術がどんどん高まっている現在、こんな事例はこれからも出てくるのではないかなと思います。

おわりに

というわけで、TED動画「ロバートラングが全く新しい時代の折り紙を折る」を見ることで、抽象化の恩恵を感じ取れるのではないかなと思います。
抽象化するってのはとてもすごいことなんですけれども、抽象化されたら具体を離れるということで、目に見える対象ではなくなっちゃうんですよね。やっぱり普段の生活で扱うものは具体的なものばかりなんで、対象が抽象的なものである「数学」という学問は「別になくても困らないし、必要ないでしょ」って結論に至っちゃう人がとても多いんでしょう。
でも、だからこそおもしろい面もあるわけで。そんなのをこれからも紹介できればなと思います。

では、お読みいただきありがとうございました。

20130703202511