summaryrefslogtreecommitdiff
path: root/ide-document-link.c
blob: 731089c7939a4c37a535a79a846b75a2fee45ca5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include "ted.h"

void document_link_clear(Ted *ted) {
	DocumentLinks *dl = &ted->document_links;
	arr_foreach_ptr(dl->links, DocumentLink, l) {
		free(l->target);
		free(l->tooltip);
	}
	arr_clear(dl->links);
	dl->requested_document = 0;
}

static bool document_link_activation_key_down(Ted *ted) {
	return ted_is_ctrl_down(ted);
}


static Rect document_link_get_rect(Ted *ted, DocumentLink *link) {
	TextBuffer *buffer = ted->active_buffer;
	DocumentLinks *dl = &ted->document_links;
	if (buffer_lsp_document_id(buffer) != dl->requested_document) {
		return (Rect){0};
	}
	
	vec2 a = buffer_pos_to_pixels(buffer, link->start);
	vec2 b = buffer_pos_to_pixels(buffer, link->end);
	if (a.y != b.y) {
		// multi-line link. let's ignore it because it'd be tough to deal with.
		return (Rect){0};
	}
	
	if (a.x > b.x) {
		// swap positions
		vec2 temp = a;
		a = b;
		b = temp;
	}
	
	float y0 = a.y;
	float char_height = text_font_char_height(buffer_font(buffer));
	return (Rect) {
		.pos = {a.x, y0},
		.size = {b.x - a.x, char_height}
	};
}

void document_link_frame(Ted *ted) {
	Settings *settings = ted_active_settings(ted);
	if (!settings->document_links) {
		document_link_clear(ted);
		return;
	}
	DocumentLinks *dl = &ted->document_links;
	
	bool key_down = document_link_activation_key_down(ted);
	if (!key_down) {
		ted_cancel_lsp_request(ted, &dl->last_request);
		document_link_clear(ted);
		return;
	}
	
	TextBuffer *buffer = ted->active_buffer;
	if (!buffer)
		return;
	
	LSP *lsp = buffer_lsp(buffer);
	if (!lsp)
		return;
	
	if (!dl->last_request.id) {
		// send the request
		LSPRequest request = {.type = LSP_REQUEST_DOCUMENT_LINK};
		LSPRequestDocumentLink *lnk = &request.data.document_link;
		lnk->document = buffer_lsp_document_id(buffer);
		dl->last_request = lsp_send_request(lsp, &request);
		dl->requested_document = lnk->document;
	}
	
	arr_foreach_ptr(dl->links, DocumentLink, l) {
		Rect r = document_link_get_rect(ted, l);
		if (rect_contains_point(r, ted->mouse_pos)) {
			ted->cursor = ted->cursor_hand;
		}
	}
}

void document_link_process_lsp_response(Ted *ted, const LSPResponse *response) {
	DocumentLinks *dl = &ted->document_links;
	if (response->request.type != LSP_REQUEST_DOCUMENT_LINK
		|| response->request.id != dl->last_request.id)
		return;
	if (!dl->last_request.id)
		return; // request was cancelled
	
	bool key_down = document_link_activation_key_down(ted);
	if (!key_down)
		return;
	TextBuffer *buffer = ted->active_buffer;
	if (!buffer)
		return;
	if (buffer_lsp_document_id(buffer) != dl->requested_document)
		return; // request was for a different document
	
	const LSPResponseDocumentLink *response_data = &response->data.document_link;
	arr_foreach_ptr(response_data->links, const LSPDocumentLink, link) {
		DocumentLink *l = arr_addp(dl->links);
		l->start = buffer_pos_from_lsp(buffer, link->range.start);
		l->end = buffer_pos_from_lsp(buffer, link->range.end);
		l->target = str_dup(lsp_response_string(response, link->target));
		const char *tooltip = lsp_response_string(response, link->tooltip);
		l->tooltip = *tooltip ? str_dup(tooltip) : NULL;
	}
}

const char *document_link_at_buffer_pos(Ted *ted, BufferPos pos) {
	DocumentLinks *dl = &ted->document_links;
	TextBuffer *buffer = ted->active_buffer;
	if (buffer_lsp_document_id(buffer) != dl->requested_document) {
		return NULL;
	}

	arr_foreach_ptr(dl->links, DocumentLink, l) {
		if (buffer_pos_cmp(pos, l->start) >= 0 && buffer_pos_cmp(pos, l->end) < 0)
			return l->target;
	}
	return NULL;
}