Browse code

Highlight matched characters

John Hawthorn authored on 26/07/2014 09:27:31
Showing 2 changed files

  • fzy.c index e3b5a28..ac3d938 100644
  • match.c index 7a6166e..3bf0e89 100644
... ...
@@ -10,6 +10,7 @@
10 10
 
11 11
 /* from match.c */
12 12
 double match(const char *needle, const char *haystack);
13
+double match_positions(const char *needle, const char *haystack, size_t *positions);
13 14
 
14 15
 #define INITIAL_CAPACITY 1
15 16
 int choices_capacity = 0;
... ...
@@ -136,6 +137,27 @@ void clear(){
136 137
 	fprintf(ttyout, "%c%c0G", 0x1b, '[');
137 138
 }
138 139
 
140
+void draw_match(const char *choice, int selected){
141
+	int n = strlen(search);
142
+	size_t positions[n + 1];
143
+	for(int i = 0; i < n + 1; i++)
144
+		positions[i] = -1;
145
+
146
+	match_positions(search, choice, &positions[0]);
147
+
148
+	for(size_t i = 0, p = 0; choice[i] != '\0'; i++){
149
+		if(positions[p] == i){
150
+			fprintf(ttyout, "%c%c33m", 0x1b, '[');
151
+			p++;
152
+		}else{
153
+			fprintf(ttyout, "%c%c39;49m", 0x1b, '[');
154
+		}
155
+		fprintf(ttyout, "%c", choice[i]);
156
+	}
157
+	fprintf(ttyout, "\n");
158
+	fprintf(ttyout, "%c%c0m", 0x1b, '[');
159
+}
160
+
139 161
 void draw(){
140 162
 	int line = 0;
141 163
 	const char *prompt = "> ";
... ...
@@ -144,9 +166,7 @@ void draw(){
144 166
 	for(size_t i = 0; line < 10 && i < choices_available; i++){
145 167
 		if(i == current_selection)
146 168
 			fprintf(ttyout, "%c%c7m", 0x1b, '[');
147
-		else
148
-			fprintf(ttyout, "%c%c0m", 0x1b, '[');
149
-		fprintf(ttyout, "%s\n", choices[choices_sorted[i]]);
169
+		draw_match(choices[choices_sorted[i]], i == current_selection);
150 170
 		line++;
151 171
 	}
152 172
 	fprintf(ttyout, "%c%c%iA", 0x1b, '[', line + 1);
... ...
@@ -30,7 +30,7 @@ void mat_print(int *mat, int n, int m){
30 30
 #define max(a, b) (((a) > (b)) ? (a) : (b))
31 31
 typedef int score_t;
32 32
 
33
-double calculate_score(const char *needle, const char *haystack){
33
+double calculate_score(const char *needle, const char *haystack, size_t *positions){
34 34
 	if(!*haystack || !*needle)
35 35
 		return SCORE_MIN;
36 36
 
... ...
@@ -85,17 +85,48 @@ double calculate_score(const char *needle, const char *haystack){
85 85
 		}
86 86
 	}
87 87
 
88
+	/* backtrace to find the positions of optimal matching */
89
+	if(positions){
90
+		for(int i = n-1, j = m-1; i >= 0; i--){
91
+			int last = M[i][j];
92
+			for(; j >= 0 && M[i][j] == last; j--){
93
+				/*
94
+				 * There may be multiple paths which result in
95
+				 * the optimal weight.
96
+				 *
97
+				 * Since we don't exit the loop on the first
98
+				 * match, positions[i] may be assigned to
99
+				 * multiple times. Since we are decrementing i
100
+				 * and j, this favours the optimal path
101
+				 * occurring earlier in the string.
102
+				 */
103
+				if(tolower(needle[i]) == tolower(haystack[j])){
104
+					positions[i] = j;
105
+				}
106
+			}
107
+		}
108
+	}
109
+
88 110
 	return (float)(M[n-1][m-1]) / (float)(n * 2 + 1);
89 111
 }
90 112
 
91
-double match(const char *needle, const char *haystack){
113
+double match_positions(const char *needle, const char *haystack, size_t *positions){
92 114
 	if(!*needle){
93 115
 		return 1.0;
94 116
 	}else if(!is_subset(needle, haystack)){
95 117
 		return SCORE_MIN;
96 118
 	}else if(!strcasecmp(needle, haystack)){
119
+		if(positions){
120
+			int n = strlen(needle);
121
+			for(int i = 0; i < n; i++)
122
+				positions[i] = i;
123
+		}
97 124
 		return 1.0;
98 125
 	}else{
99
-		return calculate_score(needle, haystack);
126
+		return calculate_score(needle, haystack, positions);
100 127
 	}
101 128
 }
129
+
130
+double match(const char *needle, const char *haystack){
131
+	return match_positions(needle, haystack, NULL);
132
+}