Skip to content

Commit 754d978

Browse files
GUACAMOLE-1961: Improve word and uri detection on double click event.
1 parent 0e860d6 commit 754d978

File tree

1 file changed

+173
-61
lines changed

1 file changed

+173
-61
lines changed

src/terminal/terminal.c

+173-61
Original file line numberDiff line numberDiff line change
@@ -1767,8 +1767,8 @@ int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
17671767

17681768
/**
17691769
* Determines if the given character is part of a word.
1770-
* Match these chars :[0-9A-Za-z\$\%\&\-\.\/\:\=\?\\_~]
1771-
* This allows a path, URL, variable name or IP address to be treated as a word.
1770+
* Match these chars :[0-9A-Za-z\$\-\.\/_~]
1771+
* This allows a path, variable name or IP address to be treated as a word.
17721772
*
17731773
* @param ascii_char
17741774
* The character to check.
@@ -1782,17 +1782,42 @@ static bool guac_terminal_is_part_of_word(int ascii_char) {
17821782
(ascii_char >= 'A' && ascii_char <= 'Z') ||
17831783
(ascii_char >= 'a' && ascii_char <= 'z') ||
17841784
(ascii_char == '$') ||
1785-
(ascii_char == '%') ||
1786-
(ascii_char == '&') ||
17871785
(ascii_char == '-') ||
17881786
(ascii_char == '.') ||
17891787
(ascii_char == '/') ||
1788+
(ascii_char == '_') ||
1789+
(ascii_char == '~'));
1790+
}
1791+
1792+
/**
1793+
* Determines if the given character is part of URI.
1794+
* Match these chars :[%\&\+:;,=\?\!\*\\\(\)\[\]#] and word chars.
1795+
*
1796+
* @param ascii_char
1797+
* The character to check.
1798+
*
1799+
* @return
1800+
* true if match a "word" char,
1801+
* false otherwise.
1802+
*/
1803+
static bool guac_terminal_is_part_of_word_or_uri(int ascii_char) {
1804+
return (guac_terminal_is_part_of_word(ascii_char) ||
1805+
(ascii_char == '%') ||
1806+
(ascii_char == '&') ||
1807+
(ascii_char == '+') ||
17901808
(ascii_char == ':') ||
1809+
(ascii_char == ';') ||
1810+
(ascii_char == ',') ||
17911811
(ascii_char == '=') ||
17921812
(ascii_char == '?') ||
1813+
(ascii_char == '!') ||
1814+
(ascii_char == '*') ||
17931815
(ascii_char == '\\') ||
1794-
(ascii_char == '_') ||
1795-
(ascii_char == '~'));
1816+
(ascii_char == '(') ||
1817+
(ascii_char == ')') ||
1818+
(ascii_char == '[') ||
1819+
(ascii_char == ']') ||
1820+
(ascii_char == '#'));
17961821
}
17971822

17981823
/**
@@ -1847,91 +1872,178 @@ static void guac_terminal_double_click(guac_terminal* terminal, int row, int col
18471872
bool (*is_part_of_word)(int) = NULL;
18481873

18491874
/* If selection is on a word, get its borders */
1850-
if (guac_terminal_is_part_of_word(cursor_char))
1851-
is_part_of_word = guac_terminal_is_part_of_word;
1875+
if (guac_terminal_is_part_of_word_or_uri(cursor_char))
1876+
is_part_of_word = guac_terminal_is_part_of_word_or_uri;
18521877

18531878
/* If selection is on a blank, get its borders */
18541879
else if (guac_terminal_is_blank(cursor_char))
18551880
is_part_of_word = guac_terminal_is_blank;
18561881

18571882
if (is_part_of_word != NULL) {
1858-
/* Get word head*/
1883+
1884+
/* Event to exit loop */
1885+
bool exit_loop = false;
1886+
18591887
do {
18601888

1861-
/* Buffer row to get */
1862-
int current_row = word_row_head;
1863-
int current_col = word_col_head;
1889+
/* Position of the word behind cursor.
1890+
* Default = col/row required to select a char if not a word and not blank. */
1891+
word_col_head = col;
1892+
word_col_tail = col;
1893+
word_row_head = row;
1894+
word_row_tail = row;
1895+
1896+
/* Get word head*/
1897+
do {
1898+
1899+
/* Buffer row to get */
1900+
int current_row = word_row_head;
1901+
int current_col = word_col_head;
1902+
1903+
/* Bound of screen reached: get previous row */
1904+
if (word_col_head == 0)
1905+
current_row--;
18641906

1865-
/* Bound of screen reached: get previous row */
1866-
if (word_col_head == 0)
1867-
current_row--;
1907+
/* Get current buffer row */
1908+
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, current_row, 0);
18681909

1869-
/* Get current buffer row */
1870-
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, current_row, 0);
1910+
/* If we are on the previous row */
1911+
if (current_row < word_row_head) {
18711912

1872-
/* If we are on the previous row */
1873-
if (current_row < word_row_head) {
1913+
/* Line not wrapped: stop, it's the word boundary */
1914+
if (!buffer_row->wrapped_row)
1915+
break;
1916+
1917+
/* Go to last column of this row */
1918+
current_col = buffer_row->length;
1919+
}
1920+
1921+
/* Get char of the current row/column */
1922+
flag = buffer_row->characters[current_col-1].value;
18741923

1875-
/* Line not wrapped: stop, it's the word boundary */
1876-
if (!buffer_row->wrapped_row)
1924+
/* Word boundary reached, stop */
1925+
if (!is_part_of_word(flag))
18771926
break;
18781927

1879-
/* Go to last column of this row */
1880-
current_col = buffer_row->length;
1881-
}
1928+
/* Store new position on previous row */
1929+
if (current_row < word_row_head) {
1930+
word_row_head = current_row;
1931+
word_col_head = current_col;
1932+
}
18821933

1883-
/* Get char of the current row/column */
1884-
flag = buffer_row->characters[current_col-1].value;
1934+
} while (word_col_head >= 0 && word_col_head--);
18851935

1886-
/* Word boundary reached, stop */
1887-
if (!is_part_of_word(flag))
1888-
break;
1936+
/* Get word tail */
1937+
do {
18891938

1890-
/* Store new position on previous row */
1891-
if (current_row < word_row_head) {
1892-
word_row_head = current_row;
1893-
word_col_head = current_col;
1894-
}
1939+
/* Get current buffer row */
1940+
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, word_row_tail, 0);
18951941

1896-
} while (word_col_head >= 0 && word_col_head--);
1942+
/* Bound of screen reached and row is wrapped: get next row */
1943+
if (word_col_tail == buffer_row->length - 1 && buffer_row->wrapped_row) {
18971944

1898-
/* Get word tail */
1899-
do {
1945+
/* Get next buffer row */
1946+
guac_terminal_buffer_row* next_buffer_row =
1947+
guac_terminal_buffer_get_row(terminal->buffer, word_row_tail + 1, 0);
19001948

1901-
/* Get current buffer row */
1902-
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, word_row_tail, 0);
1949+
/* Get first char of the next row */
1950+
flag = next_buffer_row->characters[0].value;
19031951

1904-
/* Bound of screen reached and row is wrapped: get next row */
1905-
if (word_col_tail == buffer_row->length - 1 && buffer_row->wrapped_row) {
1952+
}
19061953

1907-
/* Get next buffer row */
1908-
guac_terminal_buffer_row* next_buffer_row =
1909-
guac_terminal_buffer_get_row(terminal->buffer, word_row_tail + 1, 0);
1954+
/* Otherwise, get char of next column on current row */
1955+
else
1956+
flag = buffer_row->characters[word_col_tail+1].value;
19101957

1911-
/* Get first char of the next row */
1912-
flag = next_buffer_row->characters[0].value;
1958+
/* Word boundary reached, stop */
1959+
if (!is_part_of_word(flag))
1960+
break;
19131961

1914-
}
1962+
/* Store new position on next row */
1963+
if (word_col_tail == buffer_row->length - 1 && buffer_row->wrapped_row) {
1964+
word_row_tail++;
1965+
word_col_tail = 0;
1966+
}
19151967

1916-
/* Otherwise, get char of next column on current row */
1917-
else
1918-
flag = buffer_row->characters[word_col_tail+1].value;
1968+
/* Or go to next column of current row */
1969+
else
1970+
word_col_tail++;
19191971

1920-
/* Word boundary reached, stop */
1921-
if (!is_part_of_word(flag))
1972+
} while (word_col_tail <= buffer_row->length);
1973+
1974+
/* The following is only for URL scheme validation */
1975+
if (is_part_of_word != guac_terminal_is_part_of_word_or_uri)
19221976
break;
19231977

1924-
/* Store new position on next row */
1925-
if (word_col_tail == buffer_row->length - 1 && buffer_row->wrapped_row) {
1926-
word_row_tail++;
1927-
word_col_tail = 0;
1928-
}
1978+
/* Temp vars to avoid overwrite word_row_head and word_col_head */
1979+
int tmp_row = word_row_head;
1980+
int tmp_col = word_col_head;
19291981

1930-
/* Or go to next column of current row */
1931-
else
1932-
word_col_tail++;
1982+
/* Check for the presence of a uri scheme like /^[a-z]+\:\/{2}/ */
1983+
do {
1984+
1985+
/* Get first char of first row */
1986+
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, tmp_row, 0);
1987+
cursor_char = buffer_row->characters[tmp_col].value;
1988+
1989+
/* End of [a-z]+ part */
1990+
if (cursor_char < 'a' || cursor_char > 'z') {
1991+
1992+
/* URI scheme delimiter :// foud */
1993+
if (cursor_char == ':' &&
1994+
buffer_row->characters[tmp_col+1].value == '/' &&
1995+
buffer_row->characters[tmp_col+2].value == '/') {
1996+
1997+
/* Set exit event */
1998+
exit_loop = true;
1999+
break;
2000+
}
2001+
2002+
/* Not URI scheme */
2003+
else
2004+
break;
2005+
}
2006+
2007+
/* End of buffer row */
2008+
else if (tmp_col == buffer_row->length-1) {
2009+
2010+
/* Confinue only if current buffer row is wrapped */
2011+
if (buffer_row->wrapped_row) {
2012+
2013+
/* Stop if latest row */
2014+
if (tmp_row == word_row_tail)
2015+
break;
2016+
2017+
/* Go to next row */
2018+
tmp_row++;
2019+
2020+
/* Go to first row (-1 for auto increment on next iteration) */
2021+
tmp_col = 0;
2022+
2023+
/* Don't do further tests for this iteration */
2024+
continue;
2025+
2026+
}
2027+
2028+
/* End of selection without matching uri scheme */
2029+
else
2030+
break;
2031+
2032+
}
2033+
2034+
/* End of selection without matching uri scheme */
2035+
else if (tmp_row == word_row_tail && tmp_col == word_col_tail)
2036+
break;
2037+
2038+
tmp_col++;
2039+
//printf("tmp_row = %d | word_row_tail = %d | tmp_col = %d | word_col_tail = %d\n", tmp_row, word_row_tail, tmp_col, word_col_tail);
2040+
} while (true);
2041+
2042+
/* Get word boundaries instead of URI */
2043+
is_part_of_word = guac_terminal_is_part_of_word;
19332044

1934-
} while (word_col_tail <= buffer_row->length);
2045+
printf("!exit_loop = %d\n", !exit_loop);
2046+
} while (!exit_loop);
19352047
}
19362048

19372049
/* Select and add to clipboard the "word" */

0 commit comments

Comments
 (0)