unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* Address completion
@ 2010-04-20 21:33 Sebastian Spaeth
  2010-04-21  3:49 ` Dirk Hohndel
  0 siblings, 1 reply; 7+ messages in thread
From: Sebastian Spaeth @ 2010-04-20 21:33 UTC (permalink / raw)
  To: Notmuch development list

Oh my goodness. I just tried
http://github.com/dme/notmuch/raw/dme-play/emacs/notmuch-address.el
together with the latest version of my "addrlookup" tool (which does the
same as jkr's notmuch_addresses.py) and it just works, even in current
cworth/master.

Address completion for to: and cc: headers based on my notmuch database,
sweet! Thanks David. I only need to reverse the sort order of addrlookup
as this uses most-to-least likely (which is more intuitive than eudc's
least-to-most likely sort order).

BTW, addrlookup now first tries to find matches that I previously sent
mails to and failing that it will try to find matches for all from:
addresses that match the search.

It would be trivial to include cworth's 3rd pass (or rather the 1st one)
of checking entries with an "address book" tag first. Which would
satisfy his entry in the notmuch TODO :-). which tag would make most
sense? And if a message had such a tag, would that mean the to: or the
from: is in the address book? Carl? It's your idea :-)

Sebastian

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Address completion
  2010-04-20 21:33 Address completion Sebastian Spaeth
@ 2010-04-21  3:49 ` Dirk Hohndel
  2010-04-21  8:08   ` Sebastian Spaeth
  0 siblings, 1 reply; 7+ messages in thread
From: Dirk Hohndel @ 2010-04-21  3:49 UTC (permalink / raw)
  To: Sebastian Spaeth, Notmuch development list

On Tue, 20 Apr 2010 23:33:11 +0200, "Sebastian Spaeth" <Sebastian@SSpaeth.de> wrote:
> Oh my goodness. I just tried
> http://github.com/dme/notmuch/raw/dme-play/emacs/notmuch-address.el
> together with the latest version of my "addrlookup" tool (which does the
> same as jkr's notmuch_addresses.py) and it just works, even in current
> cworth/master.
> 
> Address completion for to: and cc: headers based on my notmuch database,
> sweet! Thanks David. I only need to reverse the sort order of addrlookup
> as this uses most-to-least likely (which is more intuitive than eudc's
> least-to-most likely sort order).
> 
> BTW, addrlookup now first tries to find matches that I previously sent
> mails to and failing that it will try to find matches for all from:
> addresses that match the search.

BTW: your version fails badly with the "Last, First" (including quotes)
names that Exchange produces... I get 

First" <email@add.res>

Not ideal :-)

/D

-- 
Dirk Hohndel
Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Address completion
  2010-04-21  3:49 ` Dirk Hohndel
@ 2010-04-21  8:08   ` Sebastian Spaeth
  2010-04-21 14:11     ` Sebastian Spaeth
  0 siblings, 1 reply; 7+ messages in thread
From: Sebastian Spaeth @ 2010-04-21  8:08 UTC (permalink / raw)
  To: Dirk Hohndel, Notmuch development list

On 2010-04-21, Dirk Hohndel wrote:
> BTW: your version fails badly with the "Last, First" (including quotes)
> names that Exchange produces... I get 
> 
> First" <email@add.res> 
> Not ideal :-)

Only in your timezone. In my timezone this has been fixed since 10
minutes at least ;-).

Sebastian 

P.S. Seriously, thanks for letting m_e know.
(darn, I can't type that 2-letter word anymore since I enabled
autocompletion to 'Sebastian Spaeth <Sebastian@SSpaeth.de>' :))

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Address completion
  2010-04-21  8:08   ` Sebastian Spaeth
@ 2010-04-21 14:11     ` Sebastian Spaeth
  0 siblings, 0 replies; 7+ messages in thread
From: Sebastian Spaeth @ 2010-04-21 14:11 UTC (permalink / raw)
  To: Dirk Hohndel, Notmuch development list

[-- Attachment #1: Type: text/plain, Size: 1961 bytes --]

David improved the notmuch-address.el glue today, and I improved my
addrlookup tool also. It is much more intelligent now:

We parse the notmuch db in up to 3 passes now where each find from
the previous pass is sorted with heigher weight, ie we will first output
addresses in our "address book" then addresses we have mailed to
previously and only than at mails we have received.
    
1st pass: Find all 'from' addresses that are in messages tagged as
      'addressbook' (this tag is configurable in .notmuch-config under
      [user] addrbook_tag=foo but uses addressbook by default)
    
2nd pass: Find all 'to','cc','bcc' addresses that match our criteria and
      that we ever sent a mail from our primary mail address
    
3rd pass: If pass1 + 2 lead to less than 10 message hits, perform a pass
      3.  Look at all email 'from' headers that match our criteria and
      use those.  We limit this, because notmuch opens all mail files
      that might match, to read the header out of them, potentially
      hurting performance a lot. So don't do pass 3 if pass 1 & 2 lead
      to likely results already.
    
Using the address book feature, you can implement simple 'blacklisting'
of emails.  If you have friends at Sun for example, you might want to
'notmuch tag +addressbook from:"oracle.com"' to give those addresses
priority over the sun.com addresses you have previously used.

Performance is still good enough. Doing a "addrlookup" which returns everyone
in my "addressbook" and all mail addresses I ever sent mail to (just
165), takes real 0m0.095s with a warm file cache. If the file cache is
cold, a search for e.g. "he" can take real	0m2.385s. The reason is
that notmuch opens all possibly matching mail files in order to retrieve
the headers.

Compile directly from vala or the attached c source. Compiling the c source works with glib2.0-dev installed:

cc -o addrlookup addrlookup.c `pkg-config --cflags --libs gobject-2.0`
-lnotmuch


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: c source file --]
[-- Type: text/x-csrc, Size: 35640 bytes --]

/* addrlookup.c generated by valac, the Vala compiler
 * generated from addrlookup.vala, do not modify */


#include <glib.h>
#include <glib-object.h>
#include <notmuch.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <gobject/gvaluecollector.h>


#define TYPE_ADDRESS_MATCHER (address_matcher_get_type ())
#define ADDRESS_MATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_ADDRESS_MATCHER, AddressMatcher))
#define ADDRESS_MATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_ADDRESS_MATCHER, AddressMatcherClass))
#define IS_ADDRESS_MATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_ADDRESS_MATCHER))
#define IS_ADDRESS_MATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_ADDRESS_MATCHER))
#define ADDRESS_MATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_ADDRESS_MATCHER, AddressMatcherClass))

typedef struct _AddressMatcher AddressMatcher;
typedef struct _AddressMatcherClass AddressMatcherClass;
typedef struct _AddressMatcherPrivate AddressMatcherPrivate;
#define _notmuch_database_close0(var) ((var == NULL) ? NULL : (var = (notmuch_database_close (var), NULL)))
#define _g_free0(var) (var = (g_free (var), NULL))
#define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL)))
#define _g_key_file_free0(var) ((var == NULL) ? NULL : (var = (g_key_file_free (var), NULL)))

#define ADDRESS_MATCHER_TYPE_MAILADDRESS_FREQ (address_matcher_mailaddress_freq_get_type ())
typedef struct _AddressMatcherMailAddress_freq AddressMatcherMailAddress_freq;
#define _g_list_free0(var) ((var == NULL) ? NULL : (var = (g_list_free (var), NULL)))
#define _g_hash_table_unref0(var) ((var == NULL) ? NULL : (var = (g_hash_table_unref (var), NULL)))
#define _g_regex_unref0(var) ((var == NULL) ? NULL : (var = (g_regex_unref (var), NULL)))
#define _g_match_info_free0(var) ((var == NULL) ? NULL : (var = (g_match_info_free (var), NULL)))
#define _0(var) ((var == NULL) ? NULL : (var = ( (var), NULL)))
#define _address_matcher_mailaddress_freq_free0(var) ((var == NULL) ? NULL : (var = (address_matcher_mailaddress_freq_free (var), NULL)))
#define __g_list_free_address_matcher_mailaddress_freq_free0(var) ((var == NULL) ? NULL : (var = (_g_list_free_address_matcher_mailaddress_freq_free (var), NULL)))
#define _g_string_free0(var) ((var == NULL) ? NULL : (var = (g_string_free (var, TRUE), NULL)))
typedef struct _ParamSpecAddressMatcher ParamSpecAddressMatcher;
#define _address_matcher_unref0(var) ((var == NULL) ? NULL : (var = (address_matcher_unref (var), NULL)))

struct _AddressMatcher {
	GTypeInstance parent_instance;
	volatile int ref_count;
	AddressMatcherPrivate * priv;
};

struct _AddressMatcherClass {
	GTypeClass parent_class;
	void (*finalize) (AddressMatcher *self);
};

struct _AddressMatcherPrivate {
	notmuch_database_t* db;
	char* user_db_path;
	char* user_primary_email;
	char* user_addrbook_tag;
};

struct _AddressMatcherMailAddress_freq {
	char* address;
	guint* occurances;
	gint occurances_length1;
	gint _occurances_size_;
};

struct _ParamSpecAddressMatcher {
	GParamSpec parent_instance;
};


static gpointer address_matcher_parent_class = NULL;

gpointer address_matcher_ref (gpointer instance);
void address_matcher_unref (gpointer instance);
GParamSpec* param_spec_address_matcher (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags);
void value_set_address_matcher (GValue* value, gpointer v_object);
void value_take_address_matcher (GValue* value, gpointer v_object);
gpointer value_get_address_matcher (const GValue* value);
GType address_matcher_get_type (void);
#define ADDRESS_MATCHER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_ADDRESS_MATCHER, AddressMatcherPrivate))
enum  {
	ADDRESS_MATCHER_DUMMY_PROPERTY
};
AddressMatcher* address_matcher_new (void);
AddressMatcher* address_matcher_construct (GType object_type);
static GType address_matcher_mailaddress_freq_get_type (void) G_GNUC_UNUSED;
static AddressMatcherMailAddress_freq* address_matcher_mailaddress_freq_dup (const AddressMatcherMailAddress_freq* self);
static void address_matcher_mailaddress_freq_free (AddressMatcherMailAddress_freq* self);
static void address_matcher_mailaddress_freq_copy (const AddressMatcherMailAddress_freq* self, AddressMatcherMailAddress_freq* dest);
static void address_matcher_mailaddress_freq_destroy (AddressMatcherMailAddress_freq* self);
static gint address_matcher_sort_by_freq (AddressMatcherMailAddress_freq* mail1, AddressMatcherMailAddress_freq* mail2);
char* address_matcher_frequent_fullname (AddressMatcher* self, GHashTable* frequencies);
GHashTable* address_matcher_addresses_by_frequency (AddressMatcher* self, notmuch_messages_t* msgs, const char* name, guint pass, GHashTable** addr2realname);
static void _vala_array_add1 (char*** array, int* length, int* size, char* value);
static void _g_list_free_address_matcher_mailaddress_freq_free (GList* self);
char** address_matcher_search_address_passes (AddressMatcher* self, notmuch_query_t** queries, int queries_length1, const char* name, int* result_length1);
static void _vala_array_add2 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value);
static void _vala_array_add3 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value);
static void _vala_array_add4 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value);
void address_matcher_run (AddressMatcher* self, const char* name);
static guint* _vala_array_dup1 (guint* self, int length);
static void address_matcher_finalize (AddressMatcher* obj);
gint _vala_main (char** args, int args_length1);
static void _vala_array_destroy (gpointer array, gint array_length, GDestroyNotify destroy_func);
static void _vala_array_free (gpointer array, gint array_length, GDestroyNotify destroy_func);



AddressMatcher* address_matcher_construct (GType object_type) {
	GError * _inner_error_;
	AddressMatcher* self;
	GKeyFile* config;
	char* home;
	_inner_error_ = NULL;
	self = (AddressMatcher*) g_type_create_instance (object_type);
	config = g_key_file_new ();
	home = g_strdup (g_getenv ("NOTMUCH_CONFIG"));
	if (home == NULL) {
		char* _tmp0_;
		home = (_tmp0_ = g_strdup (g_get_home_dir ()), _g_free0 (home), _tmp0_);
	}
	{
		char* _tmp1_;
		char* _tmp2_;
		char* _tmp3_;
		g_key_file_load_from_file (config, _tmp1_ = g_strconcat (home, "/.notmuch-config", NULL), G_KEY_FILE_NONE, &_inner_error_);
		_g_free0 (_tmp1_);
		if (_inner_error_ != NULL) {
			goto __catch0_g_error;
		}
		_tmp2_ = g_key_file_get_string (config, "database", "path", &_inner_error_);
		if (_inner_error_ != NULL) {
			goto __catch0_g_error;
		}
		self->priv->user_db_path = (_tmp3_ = _tmp2_, _g_free0 (self->priv->user_db_path), _tmp3_);
	}
	goto __finally0;
	__catch0_g_error:
	{
		GError * ex;
		ex = _inner_error_;
		_inner_error_ = NULL;
		{
			_g_error_free0 (ex);
		}
	}
	__finally0:
	if (_inner_error_ != NULL) {
		_g_key_file_free0 (config);
		_g_free0 (home);
		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
		g_clear_error (&_inner_error_);
		return NULL;
	}
	{
		char* _tmp4_;
		char* _tmp5_;
		_tmp4_ = g_key_file_get_string (config, "user", "primary_email", &_inner_error_);
		if (_inner_error_ != NULL) {
			goto __catch1_g_error;
		}
		self->priv->user_primary_email = (_tmp5_ = _tmp4_, _g_free0 (self->priv->user_primary_email), _tmp5_);
	}
	goto __finally1;
	__catch1_g_error:
	{
		GError * ex;
		ex = _inner_error_;
		_inner_error_ = NULL;
		{
			_g_error_free0 (ex);
		}
	}
	__finally1:
	if (_inner_error_ != NULL) {
		_g_key_file_free0 (config);
		_g_free0 (home);
		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
		g_clear_error (&_inner_error_);
		return NULL;
	}
	{
		char* _tmp6_;
		char* _tmp7_;
		_tmp6_ = g_key_file_get_string (config, "user", "addrbook_tag", &_inner_error_);
		if (_inner_error_ != NULL) {
			goto __catch2_g_error;
		}
		self->priv->user_addrbook_tag = (_tmp7_ = _tmp6_, _g_free0 (self->priv->user_addrbook_tag), _tmp7_);
	}
	goto __finally2;
	__catch2_g_error:
	{
		GError * ex;
		ex = _inner_error_;
		_inner_error_ = NULL;
		{
			char* _tmp8_;
			self->priv->user_addrbook_tag = (_tmp8_ = g_strdup ("addressbook"), _g_free0 (self->priv->user_addrbook_tag), _tmp8_);
			_g_error_free0 (ex);
		}
	}
	__finally2:
	if (_inner_error_ != NULL) {
		_g_key_file_free0 (config);
		_g_free0 (home);
		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
		g_clear_error (&_inner_error_);
		return NULL;
	}
	_g_key_file_free0 (config);
	_g_free0 (home);
	return self;
}


AddressMatcher* address_matcher_new (void) {
	return address_matcher_construct (TYPE_ADDRESS_MATCHER);
}


static gint address_matcher_sort_by_freq (AddressMatcherMailAddress_freq* mail1, AddressMatcherMailAddress_freq* mail2) {
	gint result = 0;
	gboolean _tmp0_ = FALSE;
	gboolean _tmp1_ = FALSE;
	gboolean _tmp2_ = FALSE;
	gboolean _tmp3_ = FALSE;
	if ((*mail1).occurances[0] == (*mail2).occurances[0]) {
		_tmp1_ = (*mail1).occurances[1] == (*mail2).occurances[1];
	} else {
		_tmp1_ = FALSE;
	}
	if (_tmp1_) {
		_tmp0_ = (*mail1).occurances[2] == (*mail2).occurances[2];
	} else {
		_tmp0_ = FALSE;
	}
	if (_tmp0_) {
		result = 0;
		return result;
	}
	if ((*mail1).occurances[0] > (*mail2).occurances[0]) {
		_tmp3_ = TRUE;
	} else {
		gboolean _tmp4_ = FALSE;
		if ((*mail1).occurances[0] == (*mail2).occurances[0]) {
			_tmp4_ = (*mail1).occurances[1] > (*mail2).occurances[1];
		} else {
			_tmp4_ = FALSE;
		}
		_tmp3_ = _tmp4_;
	}
	if (_tmp3_) {
		_tmp2_ = TRUE;
	} else {
		gboolean _tmp5_ = FALSE;
		gboolean _tmp6_ = FALSE;
		if ((*mail1).occurances[0] == (*mail2).occurances[0]) {
			_tmp6_ = (*mail1).occurances[1] == (*mail2).occurances[1];
		} else {
			_tmp6_ = FALSE;
		}
		if (_tmp6_) {
			_tmp5_ = (*mail1).occurances[2] > (*mail2).occurances[2];
		} else {
			_tmp5_ = FALSE;
		}
		_tmp2_ = _tmp5_;
	}
	if (_tmp2_) {
		result = -1;
		return result;
	}
	result = 1;
	return result;
}


static gboolean string_contains (const char* self, const char* needle) {
	gboolean result = FALSE;
	g_return_val_if_fail (self != NULL, FALSE);
	g_return_val_if_fail (needle != NULL, FALSE);
	result = strstr (self, needle) != NULL;
	return result;
}


char* address_matcher_frequent_fullname (AddressMatcher* self, GHashTable* frequencies) {
	char* result = NULL;
	guint maxfreq;
	char* fullname;
	g_return_val_if_fail (self != NULL, NULL);
	g_return_val_if_fail (frequencies != NULL, NULL);
	maxfreq = (guint) 0;
	fullname = NULL;
	{
		GList* mail_collection;
		GList* mail_it;
		mail_collection = g_hash_table_get_keys (frequencies);
		for (mail_it = mail_collection; mail_it != NULL; mail_it = mail_it->next) {
			const char* mail;
			mail = (const char*) mail_it->data;
			{
				guint freq;
				gboolean _tmp0_ = FALSE;
				gboolean _tmp1_ = FALSE;
				freq = GPOINTER_TO_UINT (g_hash_table_lookup (frequencies, mail));
				if (freq > maxfreq) {
					_tmp1_ = string_contains (mail, " ");
				} else {
					_tmp1_ = FALSE;
				}
				if (_tmp1_) {
					_tmp0_ = TRUE;
				} else {
					_tmp0_ = g_hash_table_size (frequencies) == 1;
				}
				if (_tmp0_) {
					char* _tmp2_;
					maxfreq = freq;
					fullname = (_tmp2_ = g_strdup (mail), _g_free0 (fullname), _tmp2_);
				}
			}
		}
		_g_list_free0 (mail_collection);
	}
	result = fullname;
	return result;
}


static gpointer _g_hash_table_ref0 (gpointer self) {
	return self ? g_hash_table_ref (self) : NULL;
}


GHashTable* address_matcher_addresses_by_frequency (AddressMatcher* self, notmuch_messages_t* msgs, const char* name, guint pass, GHashTable** addr2realname) {
	GHashTable* result = NULL;
	GError * _inner_error_;
	GHashTable* ht;
	GRegex* re;
	char** _tmp3_;
	gint _headers_size_;
	gint headers_length1;
	char** _tmp2_ = NULL;
	char** headers;
	g_return_val_if_fail (self != NULL, NULL);
	g_return_val_if_fail (msgs != NULL, NULL);
	g_return_val_if_fail (name != NULL, NULL);
	g_return_val_if_fail (addr2realname != NULL, NULL);
	_inner_error_ = NULL;
	ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
	re = NULL;
	{
		GRegex* _tmp0_;
		GRegex* _tmp1_;
		_tmp0_ = g_regex_new ("\\s*((\\\"(\\\\.|[^\\\\\"])*\\\"|[^,])*" "<?(?P<mail>\\b\\w+([-+.]\\w+)*\\@\\w+[-\\.\\w]*\\.([-\\.\\w]+)*\\w\\b)" \
">?)", 0, 0, &_inner_error_);
		if (_inner_error_ != NULL) {
			if (_inner_error_->domain == G_REGEX_ERROR) {
				goto __catch3_g_regex_error;
			}
			_g_hash_table_unref0 (ht);
			_g_regex_unref0 (re);
			g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
			g_clear_error (&_inner_error_);
			return NULL;
		}
		re = (_tmp1_ = _tmp0_, _g_regex_unref0 (re), _tmp1_);
	}
	goto __finally3;
	__catch3_g_regex_error:
	{
		GError * ex;
		ex = _inner_error_;
		_inner_error_ = NULL;
		{
			_g_error_free0 (ex);
		}
	}
	__finally3:
	if (_inner_error_ != NULL) {
		_g_hash_table_unref0 (ht);
		_g_regex_unref0 (re);
		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
		g_clear_error (&_inner_error_);
		return NULL;
	}
	headers = (_tmp3_ = (_tmp2_ = g_new0 (char*, 1 + 1), _tmp2_[0] = g_strdup ("from"), _tmp2_), headers_length1 = 1, _headers_size_ = headers_length1, _tmp3_);
	if (pass == 1) {
		char** _tmp5_;
		char** _tmp4_ = NULL;
		headers = (_tmp5_ = (_tmp4_ = g_new0 (char*, 3 + 1), _tmp4_[0] = g_strdup ("to"), _tmp4_[1] = g_strdup ("cc"), _tmp4_[2] = g_strdup ("bcc"), _tmp4_), headers = (_vala_array_free (headers, headers_length1, (GDestroyNotify) g_free), NULL), headers_length1 = 3, _headers_size_ = headers_length1, _tmp5_);
	}
	while (TRUE) {
		GMatchInfo* matches;
		notmuch_message_t* msg;
		if (!notmuch_messages_valid (msgs)) {
			break;
		}
		matches = NULL;
		msg = notmuch_messages_get (msgs);
		{
			char** header_collection;
			int header_collection_length1;
			int header_it;
			header_collection = headers;
			header_collection_length1 = headers_length1;
			for (header_it = 0; header_it < headers_length1; header_it = header_it + 1) {
				char* header;
				header = g_strdup (header_collection[header_it]);
				{
					char* froms;
					GMatchInfo* _tmp8_;
					gboolean _tmp7_;
					GMatchInfo* _tmp6_ = NULL;
					gboolean found;
					froms = g_strdup ((const char*) notmuch_message_get_header (msg, header));
					found = (_tmp7_ = g_regex_match (re, froms, 0, &_tmp6_), matches = (_tmp8_ = _tmp6_, _g_match_info_free0 (matches), _tmp8_), _tmp7_);
					while (TRUE) {
						char* from;
						char* addr;
						char* _tmp9_;
						char* _tmp11_;
						gboolean _tmp12_;
						gboolean is_match;
						guint occurs;
						GHashTable* realname_freq;
						if (!found) {
							break;
						}
						from = g_match_info_fetch (matches, 1);
						addr = g_match_info_fetch_named (matches, "mail");
						addr = (_tmp9_ = g_utf8_strdown (addr, -1), _g_free0 (addr), _tmp9_);
						{
							gboolean _tmp10_;
							_tmp10_ = g_match_info_next (matches, &_inner_error_);
							if (_inner_error_ != NULL) {
								if (_inner_error_->domain == G_REGEX_ERROR) {
									goto __catch4_g_regex_error;
								}
								_g_free0 (from);
								_g_free0 (addr);
								_g_free0 (header);
								_g_free0 (froms);
								_g_match_info_free0 (matches);
								_0 (msg);
								_g_hash_table_unref0 (ht);
								_g_regex_unref0 (re);
								headers = (_vala_array_free (headers, headers_length1, (GDestroyNotify) g_free), NULL);
								g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
								g_clear_error (&_inner_error_);
								return NULL;
							}
							found = _tmp10_;
						}
						goto __finally4;
						__catch4_g_regex_error:
						{
							GError * ex;
							ex = _inner_error_;
							_inner_error_ = NULL;
							{
								_g_error_free0 (ex);
							}
						}
						__finally4:
						if (_inner_error_ != NULL) {
							_g_free0 (from);
							_g_free0 (addr);
							_g_free0 (header);
							_g_free0 (froms);
							_g_match_info_free0 (matches);
							_0 (msg);
							_g_hash_table_unref0 (ht);
							_g_regex_unref0 (re);
							headers = (_vala_array_free (headers, headers_length1, (GDestroyNotify) g_free), NULL);
							g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
							g_clear_error (&_inner_error_);
							return NULL;
						}
						is_match = (_tmp12_ = g_regex_match_simple (_tmp11_ = g_strconcat ("\\b", name, NULL), from, G_REGEX_CASELESS, 0), _g_free0 (_tmp11_), _tmp12_);
						if (!is_match) {
							_g_free0 (from);
							_g_free0 (addr);
							continue;
						}
						occurs = GPOINTER_TO_UINT (g_hash_table_lookup (ht, addr)) + 1;
						g_hash_table_replace (ht, g_strdup (addr), GUINT_TO_POINTER (occurs));
						realname_freq = _g_hash_table_ref0 ((GHashTable*) g_hash_table_lookup (*addr2realname, addr));
						if (realname_freq == NULL) {
							GHashTable* _tmp13_;
							realname_freq = (_tmp13_ = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL), _g_hash_table_unref0 (realname_freq), _tmp13_);
							g_hash_table_insert (*addr2realname, g_strdup (addr), _g_hash_table_ref0 (realname_freq));
						}
						occurs = GPOINTER_TO_UINT (g_hash_table_lookup (realname_freq, from)) + 1;
						g_hash_table_replace (realname_freq, g_strdup (from), GUINT_TO_POINTER (occurs));
						_g_free0 (from);
						_g_free0 (addr);
						_g_hash_table_unref0 (realname_freq);
					}
					_g_free0 (header);
					_g_free0 (froms);
				}
			}
		}
		notmuch_message_destroy (msg);
		notmuch_messages_move_to_next (msgs);
		_g_match_info_free0 (matches);
		_0 (msg);
	}
	result = ht;
	_g_regex_unref0 (re);
	headers = (_vala_array_free (headers, headers_length1, (GDestroyNotify) g_free), NULL);
	return result;
}


static gpointer _address_matcher_mailaddress_freq_dup0 (gpointer self) {
	return self ? address_matcher_mailaddress_freq_dup (self) : NULL;
}


static void _vala_array_add1 (char*** array, int* length, int* size, char* value) {
	if ((*length) == (*size)) {
		*size = (*size) ? (2 * (*size)) : 4;
		*array = g_renew (char*, *array, (*size) + 1);
	}
	(*array)[(*length)++] = value;
	(*array)[*length] = NULL;
}


static void _g_list_free_address_matcher_mailaddress_freq_free (GList* self) {
	g_list_foreach (self, (GFunc) address_matcher_mailaddress_freq_free, NULL);
	g_list_free (self);
}


char** address_matcher_search_address_passes (AddressMatcher* self, notmuch_query_t** queries, int queries_length1, const char* name, int* result_length1) {
	char** result = NULL;
	char** _tmp0_;
	gint _return_value_size_;
	gint return_value_length1;
	char** return_value;
	GHashTable* addrfreq;
	GHashTable* addr2realname;
	guint pass;
	GList* addrs;
	char** _tmp6_;
	g_return_val_if_fail (self != NULL, NULL);
	g_return_val_if_fail (name != NULL, NULL);
	return_value = (_tmp0_ = NULL, return_value_length1 = 0, _return_value_size_ = return_value_length1, _tmp0_);
	addrfreq = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
	addr2realname = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
	pass = (guint) 0;
	{
		notmuch_query_t** q_collection;
		int q_collection_length1;
		int q_it;
		q_collection = queries;
		q_collection_length1 = queries_length1;
		for (q_it = 0; q_it < queries_length1; q_it = q_it + 1) {
			notmuch_query_t* q;
			q = q_collection[q_it];
			{
				notmuch_messages_t* msgs;
				GHashTable* ht;
				msgs = notmuch_query_search_messages (q);
				ht = address_matcher_addresses_by_frequency (self, msgs, name, pass, &addr2realname);
				{
					GList* addr_collection;
					GList* addr_it;
					addr_collection = g_hash_table_get_keys (ht);
					for (addr_it = addr_collection; addr_it != NULL; addr_it = addr_it->next) {
						const char* addr;
						addr = (const char*) addr_it->data;
						{
							AddressMatcherMailAddress_freq* freq;
							freq = _address_matcher_mailaddress_freq_dup0 ((AddressMatcherMailAddress_freq*) g_hash_table_lookup (addrfreq, addr));
							if (freq == NULL) {
								AddressMatcherMailAddress_freq* _tmp5_;
								AddressMatcherMailAddress_freq _tmp4_;
								AddressMatcherMailAddress_freq _tmp3_;
								guint* _tmp1_ = NULL;
								AddressMatcherMailAddress_freq _tmp2_ = {0};
								freq = (_tmp5_ = _address_matcher_mailaddress_freq_dup0 ((_tmp4_ = _tmp3_ = (memset (&_tmp2_, 0, sizeof (AddressMatcherMailAddress_freq)), _tmp2_.address = g_strdup (addr), _tmp2_.occurances = (_tmp1_ = g_new0 (guint, 3), _tmp1_[0] = (guint) 0, _tmp1_[1] = (guint) 0, _tmp1_[2] = (guint) 0, _tmp1_), _tmp2_.occurances_length1 = 3, _tmp2_), &_tmp4_)), _address_matcher_mailaddress_freq_free0 (freq), _tmp5_);
								address_matcher_mailaddress_freq_destroy (&_tmp3_);
							}
							(*freq).occurances[pass] = GPOINTER_TO_UINT (g_hash_table_lookup (ht, addr));
							g_hash_table_replace (addrfreq, g_strdup (addr), _address_matcher_mailaddress_freq_dup0 (freq));
							_address_matcher_mailaddress_freq_free0 (freq);
						}
					}
					_g_list_free0 (addr_collection);
				}
				notmuch_messages_destroy (msgs);
				pass = pass + ((guint) 1);
				_0 (msgs);
				_g_hash_table_unref0 (ht);
			}
		}
	}
	addrs = g_hash_table_get_values (addrfreq);
	addrs = g_list_sort (addrs, (GCompareFunc) address_matcher_sort_by_freq);
	{
		GList* addr_collection;
		GList* addr_it;
		addr_collection = addrs;
		for (addr_it = addr_collection; addr_it != NULL; addr_it = addr_it->next) {
			AddressMatcherMailAddress_freq* addr;
			addr = _address_matcher_mailaddress_freq_dup0 ((AddressMatcherMailAddress_freq*) addr_it->data);
			{
				GHashTable* freqs;
				freqs = _g_hash_table_ref0 ((GHashTable*) g_hash_table_lookup (addr2realname, (*addr).address));
				_vala_array_add1 (&return_value, &return_value_length1, &_return_value_size_, address_matcher_frequent_fullname (self, freqs));
				_address_matcher_mailaddress_freq_free0 (addr);
				_g_hash_table_unref0 (freqs);
			}
		}
	}
	result = (_tmp6_ = return_value, *result_length1 = return_value_length1, _tmp6_);
	_g_hash_table_unref0 (addrfreq);
	_g_hash_table_unref0 (addr2realname);
	__g_list_free_address_matcher_mailaddress_freq_free0 (addrs);
	return result;
	return_value = (_vala_array_free (return_value, return_value_length1, (GDestroyNotify) g_free), NULL);
	_g_hash_table_unref0 (addrfreq);
	_g_hash_table_unref0 (addr2realname);
	__g_list_free_address_matcher_mailaddress_freq_free0 (addrs);
}


static void _vala_array_add2 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value) {
	if ((*length) == (*size)) {
		*size = (*size) ? (2 * (*size)) : 4;
		*array = g_renew (notmuch_query_t*, *array, *size);
	}
	(*array)[(*length)++] = value;
}


static void _vala_array_add3 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value) {
	if ((*length) == (*size)) {
		*size = (*size) ? (2 * (*size)) : 4;
		*array = g_renew (notmuch_query_t*, *array, *size);
	}
	(*array)[(*length)++] = value;
}


static void _vala_array_add4 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value) {
	if ((*length) == (*size)) {
		*size = (*size) ? (2 * (*size)) : 4;
		*array = g_renew (notmuch_query_t*, *array, *size);
	}
	(*array)[(*length)++] = value;
}


void address_matcher_run (AddressMatcher* self, const char* name) {
	notmuch_query_t** _tmp1_;
	gint _queries_size_;
	gint queries_length1;
	notmuch_query_t** _tmp0_ = NULL;
	notmuch_query_t** queries;
	notmuch_database_t* _tmp2_;
	char* _tmp3_;
	GString* _tmp4_;
	GString* querystr;
	GString* _tmp7_;
	char** _tmp15_;
	gint __result__size_;
	gint _result__length1;
	gint _tmp14_;
	char** _result_;
	g_return_if_fail (self != NULL);
	queries = (_tmp1_ = (_tmp0_ = g_new0 (notmuch_query_t*, 0), _tmp0_), queries_length1 = 0, _queries_size_ = queries_length1, _tmp1_);
	self->priv->db = (_tmp2_ = notmuch_database_open (self->priv->user_db_path, NOTMUCH_DATABASE_MODE_READ_ONLY), _notmuch_database_close0 (self->priv->db), _tmp2_);
	querystr = (_tmp4_ = g_string_new (_tmp3_ = g_strconcat ("tag:", self->priv->user_addrbook_tag, NULL)), _g_free0 (_tmp3_), _tmp4_);
	if (name != NULL) {
		char* _tmp6_;
		char* _tmp5_;
		g_string_append (querystr, _tmp6_ = g_strconcat (_tmp5_ = g_strconcat (" and from:", name, NULL), "*", NULL));
		_g_free0 (_tmp6_);
		_g_free0 (_tmp5_);
	} else {
		name = "";
	}
	_vala_array_add2 (&queries, &queries_length1, &_queries_size_, notmuch_query_create (self->priv->db, querystr->str));
	querystr = (_tmp7_ = g_string_new (""), _g_string_free0 (querystr), _tmp7_);
	if (name != NULL) {
		char* _tmp9_;
		char* _tmp8_;
		g_string_append (querystr, _tmp9_ = g_strconcat (_tmp8_ = g_strconcat ("to:", name, NULL), "*", NULL));
		_g_free0 (_tmp9_);
		_g_free0 (_tmp8_);
	}
	if (self->priv->user_primary_email != NULL) {
		char* _tmp10_;
		g_string_append (querystr, _tmp10_ = g_strconcat (" from:", self->priv->user_primary_email, NULL));
		_g_free0 (_tmp10_);
	}
	_vala_array_add3 (&queries, &queries_length1, &_queries_size_, notmuch_query_create (self->priv->db, querystr->str));
	if ((notmuch_query_count_messages (queries[0]) + notmuch_query_count_messages (queries[1])) < 10) {
		GString* _tmp11_;
		querystr = (_tmp11_ = g_string_new (""), _g_string_free0 (querystr), _tmp11_);
		if (name != NULL) {
			char* _tmp13_;
			char* _tmp12_;
			g_string_append (querystr, _tmp13_ = g_strconcat (_tmp12_ = g_strconcat ("from:", name, NULL), "*", NULL));
			_g_free0 (_tmp13_);
			_g_free0 (_tmp12_);
		}
		_vala_array_add4 (&queries, &queries_length1, &_queries_size_, notmuch_query_create (self->priv->db, querystr->str));
	}
	_result_ = (_tmp15_ = address_matcher_search_address_passes (self, queries, queries_length1, name, &_tmp14_), _result__length1 = _tmp14_, __result__size_ = _result__length1, _tmp15_);
	{
		char** name_collection;
		int name_collection_length1;
		int name_it;
		name_collection = _result_;
		name_collection_length1 = _result__length1;
		for (name_it = 0; name_it < _result__length1; name_it = name_it + 1) {
			char* name;
			name = g_strdup (name_collection[name_it]);
			{
				fprintf (stdout, "%s\n", name);
				_g_free0 (name);
			}
		}
	}
	queries = (g_free (queries), NULL);
	_g_string_free0 (querystr);
	_result_ = (_vala_array_free (_result_, _result__length1, (GDestroyNotify) g_free), NULL);
}


static guint* _vala_array_dup1 (guint* self, int length) {
	return g_memdup (self, length * sizeof (guint));
}


static void address_matcher_mailaddress_freq_copy (const AddressMatcherMailAddress_freq* self, AddressMatcherMailAddress_freq* dest) {
	guint* _tmp0_;
	dest->address = g_strdup (self->address);
	dest->occurances = (_tmp0_ = self->occurances, (_tmp0_ == NULL) ? ((gpointer) _tmp0_) : _vala_array_dup1 (_tmp0_, (*self).occurances_length1));
	dest->occurances_length1 = self->occurances_length1;
}


static void address_matcher_mailaddress_freq_destroy (AddressMatcherMailAddress_freq* self) {
	_g_free0 (self->address);
	self->occurances = (g_free (self->occurances), NULL);
}


static AddressMatcherMailAddress_freq* address_matcher_mailaddress_freq_dup (const AddressMatcherMailAddress_freq* self) {
	AddressMatcherMailAddress_freq* dup;
	dup = g_new0 (AddressMatcherMailAddress_freq, 1);
	address_matcher_mailaddress_freq_copy (self, dup);
	return dup;
}


static void address_matcher_mailaddress_freq_free (AddressMatcherMailAddress_freq* self) {
	address_matcher_mailaddress_freq_destroy (self);
	g_free (self);
}


static GType address_matcher_mailaddress_freq_get_type (void) {
	static volatile gsize address_matcher_mailaddress_freq_type_id__volatile = 0;
	if (g_once_init_enter (&address_matcher_mailaddress_freq_type_id__volatile)) {
		GType address_matcher_mailaddress_freq_type_id;
		address_matcher_mailaddress_freq_type_id = g_boxed_type_register_static ("AddressMatcherMailAddress_freq", (GBoxedCopyFunc) address_matcher_mailaddress_freq_dup, (GBoxedFreeFunc) address_matcher_mailaddress_freq_free);
		g_once_init_leave (&address_matcher_mailaddress_freq_type_id__volatile, address_matcher_mailaddress_freq_type_id);
	}
	return address_matcher_mailaddress_freq_type_id__volatile;
}


static void value_address_matcher_init (GValue* value) {
	value->data[0].v_pointer = NULL;
}


static void value_address_matcher_free_value (GValue* value) {
	if (value->data[0].v_pointer) {
		address_matcher_unref (value->data[0].v_pointer);
	}
}


static void value_address_matcher_copy_value (const GValue* src_value, GValue* dest_value) {
	if (src_value->data[0].v_pointer) {
		dest_value->data[0].v_pointer = address_matcher_ref (src_value->data[0].v_pointer);
	} else {
		dest_value->data[0].v_pointer = NULL;
	}
}


static gpointer value_address_matcher_peek_pointer (const GValue* value) {
	return value->data[0].v_pointer;
}


static gchar* value_address_matcher_collect_value (GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) {
	if (collect_values[0].v_pointer) {
		AddressMatcher* object;
		object = collect_values[0].v_pointer;
		if (object->parent_instance.g_class == NULL) {
			return g_strconcat ("invalid unclassed object pointer for value type `", G_VALUE_TYPE_NAME (value), "'", NULL);
		} else if (!g_value_type_compatible (G_TYPE_FROM_INSTANCE (object), G_VALUE_TYPE (value))) {
			return g_strconcat ("invalid object type `", g_type_name (G_TYPE_FROM_INSTANCE (object)), "' for value type `", G_VALUE_TYPE_NAME (value), "'", NULL);
		}
		value->data[0].v_pointer = address_matcher_ref (object);
	} else {
		value->data[0].v_pointer = NULL;
	}
	return NULL;
}


static gchar* value_address_matcher_lcopy_value (const GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) {
	AddressMatcher** object_p;
	object_p = collect_values[0].v_pointer;
	if (!object_p) {
		return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
	}
	if (!value->data[0].v_pointer) {
		*object_p = NULL;
	} else if (collect_flags && G_VALUE_NOCOPY_CONTENTS) {
		*object_p = value->data[0].v_pointer;
	} else {
		*object_p = address_matcher_ref (value->data[0].v_pointer);
	}
	return NULL;
}


GParamSpec* param_spec_address_matcher (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags) {
	ParamSpecAddressMatcher* spec;
	g_return_val_if_fail (g_type_is_a (object_type, TYPE_ADDRESS_MATCHER), NULL);
	spec = g_param_spec_internal (G_TYPE_PARAM_OBJECT, name, nick, blurb, flags);
	G_PARAM_SPEC (spec)->value_type = object_type;
	return G_PARAM_SPEC (spec);
}


gpointer value_get_address_matcher (const GValue* value) {
	g_return_val_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADDRESS_MATCHER), NULL);
	return value->data[0].v_pointer;
}


void value_set_address_matcher (GValue* value, gpointer v_object) {
	AddressMatcher* old;
	g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADDRESS_MATCHER));
	old = value->data[0].v_pointer;
	if (v_object) {
		g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_ADDRESS_MATCHER));
		g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value)));
		value->data[0].v_pointer = v_object;
		address_matcher_ref (value->data[0].v_pointer);
	} else {
		value->data[0].v_pointer = NULL;
	}
	if (old) {
		address_matcher_unref (old);
	}
}


void value_take_address_matcher (GValue* value, gpointer v_object) {
	AddressMatcher* old;
	g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADDRESS_MATCHER));
	old = value->data[0].v_pointer;
	if (v_object) {
		g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_ADDRESS_MATCHER));
		g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value)));
		value->data[0].v_pointer = v_object;
	} else {
		value->data[0].v_pointer = NULL;
	}
	if (old) {
		address_matcher_unref (old);
	}
}


static void address_matcher_class_init (AddressMatcherClass * klass) {
	address_matcher_parent_class = g_type_class_peek_parent (klass);
	ADDRESS_MATCHER_CLASS (klass)->finalize = address_matcher_finalize;
	g_type_class_add_private (klass, sizeof (AddressMatcherPrivate));
}


static void address_matcher_instance_init (AddressMatcher * self) {
	self->priv = ADDRESS_MATCHER_GET_PRIVATE (self);
	self->priv->user_db_path = NULL;
	self->priv->user_primary_email = NULL;
	self->priv->user_addrbook_tag = NULL;
	self->ref_count = 1;
}


static void address_matcher_finalize (AddressMatcher* obj) {
	AddressMatcher * self;
	self = ADDRESS_MATCHER (obj);
	_notmuch_database_close0 (self->priv->db);
	_g_free0 (self->priv->user_db_path);
	_g_free0 (self->priv->user_primary_email);
	_g_free0 (self->priv->user_addrbook_tag);
}


GType address_matcher_get_type (void) {
	static volatile gsize address_matcher_type_id__volatile = 0;
	if (g_once_init_enter (&address_matcher_type_id__volatile)) {
		static const GTypeValueTable g_define_type_value_table = { value_address_matcher_init, value_address_matcher_free_value, value_address_matcher_copy_value, value_address_matcher_peek_pointer, "p", value_address_matcher_collect_value, "p", value_address_matcher_lcopy_value };
		static const GTypeInfo g_define_type_info = { sizeof (AddressMatcherClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) address_matcher_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (AddressMatcher), 0, (GInstanceInitFunc) address_matcher_instance_init, &g_define_type_value_table };
		static const GTypeFundamentalInfo g_define_type_fundamental_info = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) };
		GType address_matcher_type_id;
		address_matcher_type_id = g_type_register_fundamental (g_type_fundamental_next (), "AddressMatcher", &g_define_type_info, &g_define_type_fundamental_info, 0);
		g_once_init_leave (&address_matcher_type_id__volatile, address_matcher_type_id);
	}
	return address_matcher_type_id__volatile;
}


gpointer address_matcher_ref (gpointer instance) {
	AddressMatcher* self;
	self = instance;
	g_atomic_int_inc (&self->ref_count);
	return instance;
}


void address_matcher_unref (gpointer instance) {
	AddressMatcher* self;
	self = instance;
	if (g_atomic_int_dec_and_test (&self->ref_count)) {
		ADDRESS_MATCHER_GET_CLASS (self)->finalize (self);
		g_type_free_instance ((GTypeInstance *) self);
	}
}


gint _vala_main (char** args, int args_length1) {
	gint result = 0;
	AddressMatcher* app;
	app = address_matcher_new ();
	address_matcher_run (app, args[1]);
	result = 0;
	_address_matcher_unref0 (app);
	return result;
}


int main (int argc, char ** argv) {
	g_type_init ();
	return _vala_main (argv, argc);
}


static void _vala_array_destroy (gpointer array, gint array_length, GDestroyNotify destroy_func) {
	if ((array != NULL) && (destroy_func != NULL)) {
		int i;
		for (i = 0; i < array_length; i = i + 1) {
			if (((gpointer*) array)[i] != NULL) {
				destroy_func (((gpointer*) array)[i]);
			}
		}
	}
}


static void _vala_array_free (gpointer array, gint array_length, GDestroyNotify destroy_func) {
	_vala_array_destroy (array, array_length, destroy_func);
	g_free (array);
}





[-- Attachment #3: Type: text/plain, Size: 11 bytes --]


Sebastian

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Address Completion
@ 2017-04-09 22:12 Jörg Volbers
  2017-04-10 18:38 ` Tomi Ollila
  0 siblings, 1 reply; 7+ messages in thread
From: Jörg Volbers @ 2017-04-09 22:12 UTC (permalink / raw)
  To: notmuch

[-- Attachment #1: Type: text/plain, Size: 1199 bytes --]

Hello,

If I press <TAB> looking for an address while writing mail, 
vanilla notmuch offers me one preselected candidate. Since I use 
ivy-mode, this canididate narrows down the list of all candidates, 
forcing me to delete the initial input (C-a C-k)  before I can 
select among this list.

I currently override this behavior by setting 
notmuch-address-selection-function to my own function:

 (defun jv-notmuch--address-selection (prompt collection 
 initial-input) 
   (completing-read 
     prompt collection nil nil orig 'notmuch-address-history)) 
 (setq notmuch-address-selection-function 
 #'jv-notmuch--address-selection) 

This works as intended.

Now my question: Is this something specific to ivy-mode, and would 
it be possible to add an option which simulates the above behavior 
(basically, using the pre-set variable 'orig' instead of 
(car-options) in notmuch-address-expand-name), so that I do not 
need to insert my own function which adds no functionality?

Thanks (also for CC me via PM)

And by the way, notmuch really is great, thank you for this 
software!

Jörg


-- 
http://www.joergvolbers.de
https://fu-berlin.academia.edu/jvolbers

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 487 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Address Completion
  2017-04-09 22:12 Address Completion Jörg Volbers
@ 2017-04-10 18:38 ` Tomi Ollila
  2017-04-11  7:45   ` Jörg Volbers
  0 siblings, 1 reply; 7+ messages in thread
From: Tomi Ollila @ 2017-04-10 18:38 UTC (permalink / raw)
  To: Jörg Volbers, notmuch

On Mon, Apr 10 2017, Jörg Volbers <joerg@joergvolbers.de> wrote:

> Hello,
>
> If I press <TAB> looking for an address while writing mail, 
> vanilla notmuch offers me one preselected candidate. Since I use 
> ivy-mode, this canididate narrows down the list of all candidates, 
> forcing me to delete the initial input (C-a C-k)  before I can 
> select among this list.

Do you use 'internal completion or separate command to find completions ?

If internal, the first choice would be to install `company' package from
ELPA (I don't know how it works with separate command -- which I use,
with selection-menu). Company-mode provides nice popup where to choose
completions from.

If that is not an option, someone(tm) could provide a patch which, based
on (fboundp 'ivy-read) uses either ivy-read or completing-read, with their
specific options for completion (what I saw about ivy-read that looks
pretty good). OTOH, if someone(tm) can show (completing-read ...) which
works good as is, or when wrapped with ivy, such patches might just be
tolerated(tm).

FWIW, I've used the following code in my notmuch setup like forever...:

require 'selection-menu)
(setq notmuch-address-selection-function
      (lambda (prompt collection initial-input)
        (selection-menu "Send To:" (cons initial-input collection) t)))


Tomi


> I currently override this behavior by setting 
> notmuch-address-selection-function to my own function:
>
>  (defun jv-notmuch--address-selection (prompt collection 
>  initial-input) 
>    (completing-read 
>      prompt collection nil nil orig 'notmuch-address-history)) 
>  (setq notmuch-address-selection-function 
>  #'jv-notmuch--address-selection) 
>
> This works as intended.
>
> Now my question: Is this something specific to ivy-mode, and would 
> it be possible to add an option which simulates the above behavior 
> (basically, using the pre-set variable 'orig' instead of 
> (car-options) in notmuch-address-expand-name), so that I do not 
> need to insert my own function which adds no functionality?
>
> Thanks (also for CC me via PM)
>
> And by the way, notmuch really is great, thank you for this 
> software!
>
> Jörg
>
>
> -- 
> http://www.joergvolbers.de
> https://fu-berlin.academia.edu/jvolbers
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> https://notmuchmail.org/mailman/listinfo/notmuch

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Address Completion
  2017-04-10 18:38 ` Tomi Ollila
@ 2017-04-11  7:45   ` Jörg Volbers
  0 siblings, 0 replies; 7+ messages in thread
From: Jörg Volbers @ 2017-04-11  7:45 UTC (permalink / raw)
  To: Tomi Ollila, notmuch


[-- Attachment #1.1: Type: text/plain, Size: 3285 bytes --]

I've attached a diff which would do the trick. Don't know how to 
contribute 'officially', and I am not much of an elisp guru, so 
that might be a good starter for someone (tm) else.

I guess the problem is not restricted to ivy-mode; other 
completing interfaces like ido might pose the same problem. So it 
could be useful to generalize the approach and introduce a 
function which checks different conditions, instead of checking 
within the let construct. But there's some other stuff to do now 
for me right now. :-)

Jörg


Tomi Ollila <tomi.ollila@iki.fi> writes:

> On Mon, Apr 10 2017, Jörg Volbers <joerg@joergvolbers.de> wrote: 
> 
>> Hello, 
>> 
>> If I press <TAB> looking for an address while writing mail, 
>> vanilla notmuch offers me one preselected candidate. Since I 
>> use  ivy-mode, this canididate narrows down the list of all 
>> candidates,  forcing me to delete the initial input (C-a C-k) 
>> before I can  select among this list. 
> 
> Do you use 'internal completion or separate command to find 
> completions ? 
> 
> If internal, the first choice would be to install `company' 
> package from ELPA (I don't know how it works with separate 
> command -- which I use, with selection-menu). Company-mode 
> provides nice popup where to choose completions from. 
> 
> If that is not an option, someone(tm) could provide a patch 
> which, based on (fboundp 'ivy-read) uses either ivy-read or 
> completing-read, with their specific options for completion 
> (what I saw about ivy-read that looks pretty good). OTOH, if 
> someone(tm) can show (completing-read ...) which works good as 
> is, or when wrapped with ivy, such patches might just be 
> tolerated(tm). 
> 
> FWIW, I've used the following code in my notmuch setup like 
> forever...: 
> 
> require 'selection-menu) (setq 
> notmuch-address-selection-function 
>       (lambda (prompt collection initial-input) 
>         (selection-menu "Send To:" (cons initial-input 
>         collection) t))) 
>  
> Tomi 
>  
>> I currently override this behavior by setting 
>> notmuch-address-selection-function to my own function: 
>> 
>>  (defun jv-notmuch--address-selection (prompt collection 
>>  initial-input)  
>>    (completing-read  
>>      prompt collection nil nil orig 'notmuch-address-history))  
>>  (setq notmuch-address-selection-function 
>>  #'jv-notmuch--address-selection)  
>> 
>> This works as intended. 
>> 
>> Now my question: Is this something specific to ivy-mode, and 
>> would  it be possible to add an option which simulates the 
>> above behavior  (basically, using the pre-set variable 'orig' 
>> instead of  (car-options) in notmuch-address-expand-name), so 
>> that I do not  need to insert my own function which adds no 
>> functionality? 
>> 
>> Thanks (also for CC me via PM) 
>> 
>> And by the way, notmuch really is great, thank you for this 
>> software! 
>> 
>> Jörg 
>>  
>> --  http://www.joergvolbers.de 
>> https://fu-berlin.academia.edu/jvolbers 
>> _______________________________________________ notmuch mailing 
>> list notmuch@notmuchmail.org 
>> https://notmuchmail.org/mailman/listinfo/notmuch 

-- 
http://www.joergvolbers.de
https://fu-berlin.academia.edu/jvolbers

[-- Attachment #1.2: Datei 'diff' --]
[-- Type: application/octet-stream, Size: 1487 bytes --]

diff -u /home/jv/.emacs.d/hacknotmuch/notmuch-address-original.el /home/jv/.emacs.d/hacknotmuch/notmuch-address-modified.el
--- /home/jv/.emacs.d/hacknotmuch/notmuch-address-original.el	2017-04-11 09:30:56.612534096 +0200
+++ /home/jv/.emacs.d/hacknotmuch/notmuch-address-modified.el	2017-04-11 09:40:40.808693641 +0200
@@ -227,16 +227,16 @@
 		    ((eq num-options 1)
 		     (car options))
 		    (t
-		     (funcall notmuch-address-selection-function
-			      (format "Address (%s matches): " num-options)
-			      ;; We put the first match as the initial
-			      ;; input; we put all the matches as
-			      ;; possible completions, moving the
-			      ;; first match to the end of the list
-			      ;; makes cursor up/down in the list work
-			      ;; better.
-			      (append (cdr options) (list (car options)))
-			      (car options))))))
+		     (let ((_prompt        (format "Address (%s matches): " num-options))
+			   (_collection    (append (cdr options) (list (car options))))
+			   ;; offer different initiatial input to completing read in certain
+			   ;; special global minor modes like ivy-mode.
+			   ;; should be extended as to include ido etc.
+			   (_initial_input (if (and (fboundp 'ivy-mode) ivy-mode) orig (car options))))
+		       (funcall notmuch-address-selection-function
+				_prompt
+				_collection
+				_initial_input))))))
       (if chosen
 	  (progn
 	    (push chosen notmuch-address-history)

Diff finished.  Tue Apr 11 09:41:25 2017

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 487 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-04-11  8:04 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-04-20 21:33 Address completion Sebastian Spaeth
2010-04-21  3:49 ` Dirk Hohndel
2010-04-21  8:08   ` Sebastian Spaeth
2010-04-21 14:11     ` Sebastian Spaeth
  -- strict thread matches above, loose matches on Subject: below --
2017-04-09 22:12 Address Completion Jörg Volbers
2017-04-10 18:38 ` Tomi Ollila
2017-04-11  7:45   ` Jörg Volbers

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).