v1.0.2を公開しました。
変更点は下記の2件です。
- 入力欄でリターンキーを押下した場合にリロードしてしまう(submitしてしまう)問題修正
- アプリケーションを開くためのショートカットキー(Ctrl+Shift+O (MacはCommand+Shift+O))追加
Firefoxはアドオンのショートカットキー変更する機能が無いので、他と被ったら終わりです、、、
v1.0.2を公開しました。
変更点は下記の2件です。
Firefoxはアドオンのショートカットキー変更する機能が無いので、他と被ったら終わりです、、、
オンラインブックマーク(Google Bookmarks, Pinboard, はてなブックマーク)をインクリメンタルサーチする拡張機能を公開しました。WebExtensionsに対応したものとなり、Firefox57以降でも使えます。
ブックマークをローカルに保存しておいて利用するので、いつでも素早く検索できます。また、ショートカットキーで行やページを移動して、選択行のブックマークを開くことができます。
対応しているサービスは下記の通りです。
この拡張機能は、下記のレガシーな拡張(Firefox57以降使えなくなったもの)を置き換えるものになります。
WebExtensionsに対応するにあたって、Chrome版で提供していたものを元にしようとしましたが、Chrome版はWebSQLを利用しており、FirefoxのWebExtensionsでは利用できないため、一から作り直しました。
ブックマークは全てメモリ上で検索するようにしています。数万件くらいのブックマークならば、特に問題なく動いています。
この記事は、Redmine Advent Calendar 2017 - Qiitaの5日目の記事となります。
更新@2017-12-29
チケット一覧でエラーが発生していたので、コードを少し変更(対象のIDが存在しない場合、何も処理しない)しました。
前田さんの下記スライドみて、Redmine 4.0 で担当者欄にインクリメンタルサーチが付くことを知りました。
とっても便利そうです。4.0が待ち遠しいですね!
で、4.0が出るまで待てないので、View customize plugin for Redmineで同じようなことをやってみることにしました。
Redmineが jQuery UI 使っているので、Autocomplete | jQuery UI を使っています。イメージとしては、もともとあるプルダウンを非表示にして、担当者をインクリメンタルサーチするためのテキストボックスを追加し、非表示としているプルダウンと同期を取るような形にします。
チケット画面を対象にします。
/issues
Type:JavaScript
として下記を設定します。
$(function() { function replaceSelectToAutocomplete(selectElement) { var $select = $(selectElement); if ($select.length == 0) { return; } var options = $select.find('option[value!=""]') .map(function() { var $option = $(this); return { label: $option.text(), optionValue: $option.val() }; }) .toArray(); var $autocomplete = $('<input type="text" class="ui-autocomplete-input autocomplete" autocomplete="off">') .autocomplete({ source: options, minLength: 0, select: function(event, ui) { $select.val(ui.item.optionValue); }, change: function(event, ui) { if (ui.item != null) { return; } var inputValue = $autocomplete.val(); var matchOption = $.grep(options, function(option) { return option.label == inputValue; })[0]; if (matchOption != null) { $select.val(matchOption.optionValue); } else { $autocomplete.val(''); $select.val(''); } }}); var currentSelectValue = $select.val(); if (currentSelectValue != '') { var initAutcompleteValue = $.grep(options, function(option) { return option.optionValue == currentSelectValue; })[0].label; $autocomplete.val(initAutcompleteValue); } $select.hide() .after($autocomplete); } function setupAssignedAutocomplete() { replaceSelectToAutocomplete('#issue_assigned_to_id'); } // ステータス変更時などにDOMが差し替えられるので // フォームの内容が書き変わるたびに表示切替 var _replaceIssueFormWith = replaceIssueFormWith; replaceIssueFormWith = function(html){ _replaceIssueFormWith(html); setupAssignedAutocomplete(); }; setupAssignedAutocomplete(); });
下記のような感じになりました。
6日目は @g_maedaさんです!
RedmineからRocket.Chatに通知するプラグインを調べていたところ、redmine_messenger というプラグインがよさそうに見えました。ちなみにWebhook使っているのでSlackでも同じです。
同じようなもので、 redmine-slack があります。機能的にはほとんど変わりません。というか、redmine_messenger は、redmine-slack のコードも元にしているとのことです。
redmine-slack と比べて良いと思ったのは、、
プロジェクト毎の設定画面は下記の通りです。(最後の方が切れてしまっていますが、チケットのあとにWikiの設定があります)
大量のテキストからトークンを切り出す場合(形態素解析など)に、同じ内容のトークンが大量に発生することがあります。
このトークンが不変なオブジェクトであるならば、同じ内容のトークンは1つのオブジェクトを使いまわす、すなわちキャッシュすることによって、メモリ使用量を減らせそうだなということで試してみました。
Javaでの検証となります。 アルファベット以外を区切り文字とし、小文字にしたアルファベットの文字列をトークンとするといった簡易的なコードで検証します。
キャッシュなしは下記のようなコードになります。
public class Tokenizer { public List<String> tokenize(String text) { String[] tokens = text.split("[^a-zA-Z]+"); return Stream.of(tokens) .map(String::toLowerCase) .collect(Collectors.toList()); } }
キャッシュありでは、トークンのキャッシュのために、HashMapを使用します。Key、Valueともにトークンが入ります。(マルチスレッドで使用されることを想定していないコードになっているので注意)
public class CachedTokenizer { private HashMap<String, String> tokenCache = new HashMap<>(); public List<String> tokenize(String text) { String[] tokens = text.split("[^a-zA-Z]+"); return Stream.of(tokens) .map(String::toLowerCase) .map(token -> tokenCache.computeIfAbsent(token, key -> key)) .collect(Collectors.toList()); } }
英文のテキストを使用して検証を行いました。 言語処理100本ノック 2015 にて配布されている下記コーパスの先頭50万行を使用させていただきました。
2015年1月12日時点のWikipedia記事データベースのダンプ(英語)うち,約400語以上で構成される記事の中から,ランダムに1/10サンプリングした105,752記事のテキストをbzip2形式で圧縮したものです.このファイルはクリエイティブ・コモンズ 表示-継承 3.0 非移植のライセンスで配布されています.
サイズとしては140MBとなり、出現トークン数は重複ありで2300万、重複を取り除くと28万ほどとなりました。
リソースの状況は、Java Flight Recorderを使用して確認しています。
キャッシュ無しの場合の実行結果です。 メモリは最大で1.45GBまで使用されています。処理時間は27秒ほどでした。
キャッシュありの場合の実行結果です。 メモリは最大で792MBまで使用されています。処理時間は10秒ほどでした。
メモリ使用量で約半分、処理時間も半分以下になっています。処理時間が短縮されたのはGCにかかる時間が減ったためのようです。(使用するメモリ量も減ったので)
.map(token -> token.intern()) でも同じ効果になると思います。こちらであれば、JVM 全体でキャッシュしてくれます(システム全体の文字列量が多いと遅くなるかもしれません…)
— Yuji@いつだって眠い (@YujiSoftware) 2017年10月28日
と教えていただいたので、internを使って試してみたところ、同じような効果を得られました。
同じトークンの出現頻度が多ければ、キャッシュすることによってメモリ使用量を抑える効果があることがわかりました。
同じトークンが少ない場合には、効果が薄い(逆にHashMap分余計にメモリを使うことになりかねない)ため、扱うデータの性質などによって、キャッシュするかどうか考えたほうがよさそうです。(小さなテキストでも試しましたが、ほとんど効果を得られませんでした)
CSSのcolumn-count
などを使って段組した場合、要素の途中で折り返しをして欲しくなくて、break-inside: avoid;
を指定していました。
Chromeはこれで意図した動きになりましたが、Firefox(確認したのはFirefox 56)ではサポートされておらず、折り返しを禁止することができませんでした。
どうしようかと悩んでいたところ、似たようなプロパティとしてpage-break-inside
があることを知り、試してみたところpage-break-inside: avoid;
で同じ結果を得ることができました。
ShortcutKey2URL for Chrome という、Google Chrome向けの拡張機能を公開しました。
ShortcutKey2URL は、ショートカットキーを使用してURLを開いたり、移動したり、JavaScriptを実行できる拡張機能です。
スタートアップキー(デフォルトはCtrl+Period、MacだけCommand+Comma)であらかじめ設定しておいた動作の一覧を表示し、次のキーでその動作を実行します。この2段階動作が、ShortcutKey2URLの特徴です。
キーとして設定しておける文字は1文字に限定されません。2文字以上で設定することも可能です。これによって、キーの自由度が高まります。たとえば、Googleのサービスは先頭1文字は"G"として、2文字目で各サービスを表すといったような、階層を持たせることができます。
また動作として設定できるものには、下記のようなものがあります。
ぜひお試しください!
ShortcutKey2URLは、もともとFirefoxのアドオンとして、10年くらい前に公開したものです。
自分自身でもずっと愛用していて、欠かせないアドオンです。
Firefoxの57にて、WebExtensions対応のアドオン以外は無効になるため、このままだとShortcutKey2URLは使えなくなってしまいます。他の利用者の方からも、WebExtensions対応して欲しい!といった要望をもらったので、1ヶ月ほど前からコツコツとWebExtensions版を作り始めました。
ただ、いきなりFirefoxのWebExtensionsのバグを踏んでしまい、その対応まっていると先に進まない、、ということで、Google Chromeの拡張機能で同等のものを作り始めて、今回のリリースとなりました。 同じ名前ではありますが、一から作り直す形になっています。既存のコードはまったく役に立たないというか、同じ方法では実現できないため、まずは実現方法から考え直した感じです。
Firefox版も、これから対応を始めて、Firefox57のリリースまでには間に合わせたいと思っています。