[geany/geany] b07c8b: Merge branch 'scintilla/update-373'

Colomban Wendling git-noreply at xxxxx
Sun Feb 26 19:24:13 UTC 2017


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Sun, 26 Feb 2017 19:24:13 UTC
Commit:      b07c8b01c08e0f85b0122211129c952b2f22aecb
             https://github.com/geany/geany/commit/b07c8b01c08e0f85b0122211129c952b2f22aecb

Log Message:
-----------
Merge branch 'scintilla/update-373'

Update Scintilla to version 3.7.3 plus a fix for a regression on it.

Closes #1320.


Modified Paths:
--------------
    data/filedefs/filetypes.python
    scintilla/gtk/PlatGTK.cxx
    scintilla/gtk/ScintillaGTK.cxx
    scintilla/gtk/ScintillaGTK.h
    scintilla/include/SciLexer.h
    scintilla/include/Scintilla.iface
    scintilla/lexers/LexDiff.cxx
    scintilla/lexers/LexLua.cxx
    scintilla/lexers/LexMatlab.cxx
    scintilla/lexers/LexPython.cxx
    scintilla/lexlib/WordList.cxx
    scintilla/src/Document.cxx
    scintilla/src/Document.h
    scintilla/src/EditView.cxx
    scintilla/src/EditView.h
    scintilla/src/Editor.cxx
    scintilla/src/ScintillaBase.cxx
    scintilla/src/XPM.cxx
    scintilla/src/XPM.h
    scintilla/version.txt
    src/highlighting.c
    src/highlightingmappings.h

Modified: data/filedefs/filetypes.python
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -17,6 +17,10 @@ commentblock=comment
 stringeol=string_eol
 word2=keyword_2
 decorator=decorator
+fstring=string_1
+fcharacter=character
+ftriple=string_2
+ftripledouble=string_2
 
 [keywords]
 # all items must be in one line


Modified: scintilla/gtk/PlatGTK.cxx
62 lines changed, 51 insertions(+), 11 deletions(-)
===================================================================
@@ -1034,22 +1034,44 @@ void Window::SetPosition(PRectangle rc) {
 	gtk_widget_size_allocate(PWidget(wid), &alloc);
 }
 
+namespace {
+
+GdkRectangle MonitorRectangleForWidget(GtkWidget *wid) {
+	GdkWindow *wnd = WindowFromWidget(wid);
+	GdkRectangle rcScreen = GdkRectangle();
+#if GTK_CHECK_VERSION(3,22,0)
+	GdkDisplay *pdisplay = gtk_widget_get_display(wid);
+	GdkMonitor *monitor = gdk_display_get_monitor_at_window(pdisplay, wnd);
+	gdk_monitor_get_geometry(monitor, &rcScreen);
+#else
+	GdkScreen* screen = gtk_widget_get_screen(wid);
+	gint monitor_num = gdk_screen_get_monitor_at_window(screen, wnd);
+	gdk_screen_get_monitor_geometry(screen, monitor_num, &rcScreen);
+#endif
+	return rcScreen;
+}
+
+}
+
 void Window::SetPositionRelative(PRectangle rc, Window relativeTo) {
 	int ox = 0;
 	int oy = 0;
-	gdk_window_get_origin(WindowFromWidget(PWidget(relativeTo.wid)), &ox, &oy);
+	GdkWindow *wndRelativeTo = WindowFromWidget(PWidget(relativeTo.wid));
+	gdk_window_get_origin(wndRelativeTo, &ox, &oy);
 	ox += rc.left;
 	if (ox < 0)
 		ox = 0;
 	oy += rc.top;
 	if (oy < 0)
 		oy = 0;
 
+	GdkRectangle rcScreen = MonitorRectangleForWidget(PWidget(relativeTo.wid));
+
 	/* do some corrections to fit into screen */
 	int sizex = rc.right - rc.left;
 	int sizey = rc.bottom - rc.top;
-	int screenWidth = gdk_screen_width();
-	int screenHeight = gdk_screen_height();
+	const int screenWidth = rcScreen.width;
+	const int screenHeight = rcScreen.height;
 	if (sizex > screenWidth)
 		ox = 0; /* the best we can do */
 	else if (ox + sizex > screenWidth)
@@ -1145,13 +1167,19 @@ PRectangle Window::GetMonitorRect(Point pt) {
 
 	gdk_window_get_origin(WindowFromWidget(PWidget(wid)), &x_offset, &y_offset);
 
-	GdkScreen* screen;
-	gint monitor_num;
 	GdkRectangle rect;
 
-	screen = gtk_widget_get_screen(PWidget(wid));
-	monitor_num = gdk_screen_get_monitor_at_point(screen, pt.x + x_offset, pt.y + y_offset);
+#if GTK_CHECK_VERSION(3,22,0)
+	GdkDisplay *pdisplay = gtk_widget_get_display(PWidget(wid));
+	GdkMonitor *monitor = gdk_display_get_monitor_at_point(pdisplay,
+		pt.x + x_offset, pt.y + y_offset);
+	gdk_monitor_get_geometry(monitor, &rect);
+#else
+	GdkScreen* screen = gtk_widget_get_screen(PWidget(wid));
+	gint monitor_num = gdk_screen_get_monitor_at_point(screen,
+		pt.x + x_offset, pt.y + y_offset);
 	gdk_screen_get_monitor_geometry(screen, monitor_num, &rect);
+#endif
 	rect.x -= x_offset;
 	rect.y -= y_offset;
 	return PRectangle(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
@@ -1401,7 +1429,7 @@ static void StyleSet(GtkWidget *w, GtkStyle*, void*) {
 #endif
 }
 
-void ListBoxX::Create(Window &, int, Point, int, bool, int) {
+void ListBoxX::Create(Window &parent, int, Point, int, bool, int) {
 	if (widCached != 0) {
 		wid = widCached;
 		return;
@@ -1475,6 +1503,10 @@ void ListBoxX::Create(Window &, int, Point, int, bool, int) {
 	gtk_widget_show(widget);
 	g_signal_connect(G_OBJECT(widget), "button_press_event",
 	                   G_CALLBACK(ButtonPress), this);
+
+	GtkWidget *top = gtk_widget_get_toplevel(static_cast<GtkWidget *>(parent.GetID()));
+	gtk_window_set_transient_for(GTK_WINDOW(static_cast<GtkWidget *>(wid)),
+		GTK_WINDOW(top));
 }
 
 void ListBoxX::SetFont(Font &scint_font) {
@@ -1882,17 +1914,24 @@ void Menu::Destroy() {
 	mid = 0;
 }
 
+#if !GTK_CHECK_VERSION(3,22,0)
 static void MenuPositionFunc(GtkMenu *, gint *x, gint *y, gboolean *, gpointer userData) {
 	sptr_t intFromPointer = GPOINTER_TO_INT(userData);
 	*x = intFromPointer & 0xffff;
 	*y = intFromPointer >> 16;
 }
+#endif
 
-void Menu::Show(Point pt, Window &) {
-	int screenHeight = gdk_screen_height();
-	int screenWidth = gdk_screen_width();
+void Menu::Show(Point pt, Window &wnd) {
 	GtkMenu *widget = static_cast<GtkMenu *>(mid);
 	gtk_widget_show_all(GTK_WIDGET(widget));
+#if GTK_CHECK_VERSION(3,22,0)
+	// Rely on GTK+ to do the right thing with positioning
+	gtk_menu_popup_at_pointer(widget, NULL);
+#else
+	GdkRectangle rcScreen = MonitorRectangleForWidget(PWidget(wnd.GetID()));
+	const int screenWidth = rcScreen.width;
+	const int screenHeight = rcScreen.height;
 	GtkRequisition requisition;
 #if GTK_CHECK_VERSION(3,0,0)
 	gtk_widget_get_preferred_size(GTK_WIDGET(widget), NULL, &requisition);
@@ -1908,6 +1947,7 @@ void Menu::Show(Point pt, Window &) {
 	gtk_menu_popup(widget, NULL, NULL, MenuPositionFunc,
 		GINT_TO_POINTER((static_cast<int>(pt.y) << 16) | static_cast<int>(pt.x)), 0,
 		gtk_get_current_event_time());
+#endif
 }
 
 ElapsedTime::ElapsedTime() {


Modified: scintilla/gtk/ScintillaGTK.cxx
42 lines changed, 41 insertions(+), 1 deletions(-)
===================================================================
@@ -7,6 +7,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <time.h>
+#include <math.h>
 #include <assert.h>
 #include <ctype.h>
 
@@ -22,6 +23,9 @@
 #include <gdk/gdk.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
+#if defined(GDK_WINDOWING_WAYLAND)
+#include <gdk/gdkwayland.h>
+#endif
 
 #if defined(__WIN32__) || defined(_MSC_VER)
 #include <windows.h>
@@ -165,6 +169,8 @@ ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) :
 		im_context(NULL), lastNonCommonScript(PANGO_SCRIPT_INVALID_CODE),
 		lastWheelMouseDirection(0),
 		wheelMouseIntensity(0),
+		smoothScrollY(0),
+		smoothScrollX(0),
 		rgnUpdate(0),
 		repaintFullWindow(false),
 		styleIdleID(0),
@@ -545,11 +551,21 @@ void ScintillaGTK::Initialise() {
 	parentClass = reinterpret_cast<GtkWidgetClass *>(
 	                  g_type_class_ref(gtk_container_get_type()));
 
+	gint maskSmooth = 0;
+#if defined(GDK_WINDOWING_WAYLAND)
+	GdkDisplay *pdisplay = gdk_display_get_default();
+	if (GDK_IS_WAYLAND_DISPLAY(pdisplay)) {
+		// On Wayland, touch pads only produce smooth scroll events
+		maskSmooth = GDK_SMOOTH_SCROLL_MASK;
+	}
+#endif
+
 	gtk_widget_set_can_focus(PWidget(wMain), TRUE);
 	gtk_widget_set_sensitive(PWidget(wMain), TRUE);
 	gtk_widget_set_events(PWidget(wMain),
 	                      GDK_EXPOSURE_MASK
 	                      | GDK_SCROLL_MASK
+	                      | maskSmooth
 	                      | GDK_STRUCTURE_MASK
 	                      | GDK_KEY_PRESS_MASK
 	                      | GDK_KEY_RELEASE_MASK
@@ -1301,6 +1317,9 @@ void ScintillaGTK::CreateCallTipWindow(PRectangle rc) {
 				   G_CALLBACK(ScintillaGTK::PressCT), static_cast<void *>(this));
 		gtk_widget_set_events(widcdrw,
 			GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
+		GtkWidget *top = gtk_widget_get_toplevel(static_cast<GtkWidget *>(wMain.GetID()));
+		gtk_window_set_transient_for(GTK_WINDOW(static_cast<GtkWidget *>(PWidget(ct.wCallTip))),
+			GTK_WINDOW(top));
 	}
 	gtk_widget_set_size_request(PWidget(ct.wDraw), rc.Width(), rc.Height());
 	ct.wDraw.Show();
@@ -1781,6 +1800,25 @@ gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) {
 		if (widget == NULL || event == NULL)
 			return FALSE;
 
+#if defined(GDK_WINDOWING_WAYLAND)
+		if (event->direction == GDK_SCROLL_SMOOTH && GDK_IS_WAYLAND_WINDOW (event->window)) {
+			const int smoothScrollFactor = 4;
+			sciThis->smoothScrollY += event->delta_y * smoothScrollFactor;
+			sciThis->smoothScrollX += event->delta_x * smoothScrollFactor;;
+			if (ABS(sciThis->smoothScrollY) >= 1.0) {
+				const int scrollLines = trunc(sciThis->smoothScrollY);
+				sciThis->ScrollTo(sciThis->topLine + scrollLines);
+				sciThis->smoothScrollY -= scrollLines;
+			}
+			if (ABS(sciThis->smoothScrollX) >= 1.0) {
+				const int scrollPixels = trunc(sciThis->smoothScrollX);
+				sciThis->HorizontalScrollTo(sciThis->xOffset + scrollPixels);
+				sciThis->smoothScrollX -= scrollPixels;
+			}
+			return TRUE;
+		}
+#endif
+
 		// Compute amount and direction to scroll (even tho on win32 there is
 		// intensity of scrolling info in the native message, gtk doesn't
 		// support this so we simulate similarly adaptive scrolling)
@@ -2397,7 +2435,9 @@ void ScintillaGTK::StyleSetText(GtkWidget *widget, GtkStyle *, void*) {
 void ScintillaGTK::RealizeText(GtkWidget *widget, void*) {
 	// Set NULL background to avoid automatic clearing so Scintilla responsible for all drawing
 	if (WindowFromWidget(widget)) {
-#if GTK_CHECK_VERSION(3,0,0)
+#if GTK_CHECK_VERSION(3,22,0)
+		// Appears unnecessary
+#elif GTK_CHECK_VERSION(3,0,0)
 		gdk_window_set_background_pattern(WindowFromWidget(widget), NULL);
 #else
 		gdk_window_set_back_pixmap(WindowFromWidget(widget), NULL, FALSE);


Modified: scintilla/gtk/ScintillaGTK.h
2 lines changed, 2 insertions(+), 0 deletions(-)
===================================================================
@@ -57,6 +57,8 @@ class ScintillaGTK : public ScintillaBase {
 	GTimeVal lastWheelMouseTime;
 	gint lastWheelMouseDirection;
 	gint wheelMouseIntensity;
+	gdouble smoothScrollY;
+	gdouble smoothScrollX;
 
 #if GTK_CHECK_VERSION(3,0,0)
 	cairo_rectangle_list_t *rgnUpdate;


Modified: scintilla/include/SciLexer.h
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -151,6 +151,10 @@
 #define SCE_P_STRINGEOL 13
 #define SCE_P_WORD2 14
 #define SCE_P_DECORATOR 15
+#define SCE_P_FSTRING 16
+#define SCE_P_FCHARACTER 17
+#define SCE_P_FTRIPLE 18
+#define SCE_P_FTRIPLEDOUBLE 19
 #define SCE_C_DEFAULT 0
 #define SCE_C_COMMENT 1
 #define SCE_C_COMMENTLINE 2


Modified: scintilla/include/Scintilla.iface
8 lines changed, 6 insertions(+), 2 deletions(-)
===================================================================
@@ -2354,10 +2354,10 @@ get bool GetSelectionEmpty=2650(,)
 fun void ClearSelections=2571(,)
 
 # Set a simple selection
-fun int SetSelection=2572(position caret, position anchor)
+fun void SetSelection=2572(position caret, position anchor)
 
 # Add a selection
-fun int AddSelection=2573(position caret, position anchor)
+fun void AddSelection=2573(position caret, position anchor)
 
 # Drop one selection
 fun void DropSelectionN=2671(int selection,)
@@ -2914,6 +2914,10 @@ val SCE_P_COMMENTBLOCK=12
 val SCE_P_STRINGEOL=13
 val SCE_P_WORD2=14
 val SCE_P_DECORATOR=15
+val SCE_P_FSTRING=16
+val SCE_P_FCHARACTER=17
+val SCE_P_FTRIPLE=18
+val SCE_P_FTRIPLEDOUBLE=19
 # Lexical states for SCLEX_CPP, SCLEX_BULLANT, SCLEX_COBOL, SCLEX_TACL, SCLEX_TAL
 lex Cpp=SCLEX_CPP SCE_C_
 lex BullAnt=SCLEX_BULLANT SCE_C_


Modified: scintilla/lexers/LexDiff.cxx
4 lines changed, 3 insertions(+), 1 deletions(-)
===================================================================
@@ -51,8 +51,10 @@ static void ColouriseDiffLine(char *lineBuffer, Sci_Position endLine, Accessor &
 			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 		else if (lineBuffer[3] == '\r' || lineBuffer[3] == '\n')
 			styler.ColourTo(endLine, SCE_DIFF_POSITION);
-		else
+		else if (lineBuffer[3] == ' ')
 			styler.ColourTo(endLine, SCE_DIFF_HEADER);
+		else
+			styler.ColourTo(endLine, SCE_DIFF_DELETED);
 	} else if (0 == strncmp(lineBuffer, "+++ ", 4)) {
 		// I don't know of any diff where "+++ " is a position marker, but for
 		// consistency, do the same as with "--- " and "*** ".


Modified: scintilla/lexers/LexLua.cxx
4 lines changed, 2 insertions(+), 2 deletions(-)
===================================================================
@@ -89,8 +89,8 @@ static void ColouriseLuaDoc(
 	}
 
 	StyleContext sc(startPos, length, initStyle, styler);
-	if (startPos == 0 && sc.ch == '#') {
-		// shbang line: # is a comment only if first char of the script
+	if (startPos == 0 && sc.ch == '#' && sc.chNext == '!') {
+		// shbang line: "#!" is a comment only if located at the start of the script
 		sc.SetState(SCE_LUA_COMMENTLINE);
 	}
 	for (; sc.More(); sc.Forward()) {


Modified: scintilla/lexers/LexMatlab.cxx
33 lines changed, 24 insertions(+), 9 deletions(-)
===================================================================
@@ -18,6 +18,9 @@
  **
  ** Changes by John Donoghue 2016/11/15
  **   - update matlab code folding
+ **
+ ** Changes by John Donoghue 2017/01/18
+ **   - update matlab block comment detection
  **/
 // Copyright 1998-2001 by Neil Hodgson <neilh at scintilla.org>
 // The License.txt file describes the conditions under which this software may be distributed.
@@ -73,6 +76,15 @@ static int CheckKeywordFoldPoint(char *str) {
 	return 0;
 }
 
+static bool IsSpaceToEOL(Sci_Position startPos, Accessor &styler) {
+	Sci_Position line = styler.GetLine(startPos);
+	Sci_Position eol_pos = styler.LineStart(line + 1) - 1;
+	for (Sci_Position i = startPos; i < eol_pos; i++) {
+		char ch = styler[i];
+		if(!IsASpace(ch)) return false;
+	}
+	return true;
+}
 
 static void ColouriseMatlabOctaveDoc(
             Sci_PositionU startPos, Sci_Position length, int initStyle,
@@ -180,7 +192,7 @@ static void ColouriseMatlabOctaveDoc(
 			}
 		} else if (sc.state == SCE_MATLAB_COMMENT) {
 			// end or start of a nested a block comment?
-			if( IsCommentChar(sc.ch) && sc.chNext == '}' && nonSpaceColumn == column) {
+			if( IsCommentChar(sc.ch) && sc.chNext == '}' && nonSpaceColumn == column && IsSpaceToEOL(sc.currentPos+2, styler)) {
                            	if(commentDepth > 0) commentDepth --;
 
 				curLine = styler.GetLine(sc.currentPos);
@@ -192,7 +204,7 @@ static void ColouriseMatlabOctaveDoc(
 					transpose = false;
 				}
                         }
-                        else if( IsCommentChar(sc.ch) && sc.chNext == '{' && nonSpaceColumn == column)
+                        else if( IsCommentChar(sc.ch) && sc.chNext == '{' && nonSpaceColumn == column && IsSpaceToEOL(sc.currentPos+2, styler))
                         {
  				commentDepth ++;
 
@@ -214,8 +226,11 @@ static void ColouriseMatlabOctaveDoc(
 		if (sc.state == SCE_MATLAB_DEFAULT) {
 			if (IsCommentChar(sc.ch)) {
 				// ncrement depth if we are a block comment
-				if(sc.chNext == '{' && nonSpaceColumn == column)
-					commentDepth ++;
+				if(sc.chNext == '{' && nonSpaceColumn == column) {
+					if(IsSpaceToEOL(sc.currentPos+2, styler)) {
+						commentDepth ++;
+					}
+				}
 				curLine = styler.GetLine(sc.currentPos);
 				styler.SetLineState(curLine, commentDepth);
 				sc.SetState(SCE_MATLAB_COMMENT);
@@ -284,13 +299,13 @@ static void FoldMatlabOctaveDoc(Sci_PositionU startPos, Sci_Position length, int
 		style = styleNext;
 		styleNext = styler.StyleAt(i + 1);
 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
-	
+
 		// a line that starts with a comment
 		if (style == SCE_MATLAB_COMMENT && IsComment(ch) && visibleChars == 0) {
-			// start/end of block comment 
-			if (chNext == '{')
+			// start/end of block comment
+			if (chNext == '{' && IsSpaceToEOL(i+2, styler))
 				levelNext ++;
-			if (chNext == '}')
+			if (chNext == '}' && IsSpaceToEOL(i+2, styler))
 				levelNext --;
 		}
 		// keyword
@@ -303,7 +318,7 @@ static void FoldMatlabOctaveDoc(Sci_PositionU startPos, Sci_Position length, int
 			if (styleNext !=  SCE_MATLAB_KEYWORD) {
 				word[wordlen] = '\0';
 				wordlen = 0;
-	
+
 				levelNext += CheckKeywordFoldPoint(word);
  			}
 		}


Modified: scintilla/lexers/LexPython.cxx
175 lines changed, 139 insertions(+), 36 deletions(-)
===================================================================
@@ -20,11 +20,13 @@
 #include "Scintilla.h"
 #include "SciLexer.h"
 
+#include "StringCopy.h"
 #include "WordList.h"
 #include "LexAccessor.h"
 #include "Accessor.h"
 #include "StyleContext.h"
 #include "CharacterSet.h"
+#include "CharacterCategory.h"
 #include "LexerModule.h"
 #include "OptionSet.h"
 #include "SubStyles.h"
@@ -39,7 +41,7 @@ namespace {
 /* kwCDef, kwCTypeName only used for Cython */
 enum kwType { kwOther, kwClass, kwDef, kwImport, kwCDef, kwCTypeName, kwCPDef };
 
-enum literalsAllowed { litNone = 0, litU = 1, litB = 2 };
+enum literalsAllowed { litNone = 0, litU = 1, litB = 2, litF = 4 };
 
 const int indicatorWhitespace = 1;
 
@@ -50,7 +52,8 @@ bool IsPyComment(Accessor &styler, Sci_Position pos, Sci_Position len) {
 bool IsPyStringTypeChar(int ch, literalsAllowed allowed) {
 	return
 		((allowed & litB) && (ch == 'b' || ch == 'B')) ||
-		((allowed & litU) && (ch == 'u' || ch == 'U'));
+		((allowed & litU) && (ch == 'u' || ch == 'U')) ||
+		((allowed & litF) && (ch == 'f' || ch == 'F'));
 }
 
 bool IsPyStringStart(int ch, int chNext, int chNext2, literalsAllowed allowed) {
@@ -68,12 +71,44 @@ bool IsPyStringStart(int ch, int chNext, int chNext2, literalsAllowed allowed) {
 	return false;
 }
 
+bool IsPyFStringState(int st) {
+	return ((st == SCE_P_FCHARACTER) || (st == SCE_P_FSTRING) ||
+		(st == SCE_P_FTRIPLE) || (st == SCE_P_FTRIPLEDOUBLE));
+}
+
+bool IsPySingleQuoteStringState(int st) {
+	return ((st == SCE_P_CHARACTER) || (st == SCE_P_STRING) ||
+		(st == SCE_P_FCHARACTER) || (st == SCE_P_FSTRING));
+}
+
+bool IsPyTripleQuoteStringState(int st) {
+	return ((st == SCE_P_TRIPLE) || (st == SCE_P_TRIPLEDOUBLE) ||
+		(st == SCE_P_FTRIPLE) || (st == SCE_P_FTRIPLEDOUBLE));
+}
+
+void PushStateToStack(int state, int *stack, int stackSize) {
+	for (int i = stackSize-1; i > 0; i--) {
+		stack[i] = stack[i-1];
+	}
+	stack[0] = state;
+}
+
+int PopFromStateStack(int *stack, int stackSize) {
+	int top = stack[0];
+	for (int i = 0; i < stackSize - 1; i++) {
+		stack[i] = stack[i+1];
+	}
+	stack[stackSize-1] = 0;
+	return top;
+}
+
 /* Return the state to use for the string starting at i; *nextIndex will be set to the first index following the quote(s) */
 int GetPyStringState(Accessor &styler, Sci_Position i, Sci_PositionU *nextIndex, literalsAllowed allowed) {
 	char ch = styler.SafeGetCharAt(i);
 	char chNext = styler.SafeGetCharAt(i + 1);
+	int firstIsF = (ch == 'f' || ch == 'F');
 
-	// Advance beyond r, u, or ur prefix (or r, b, or br in Python 3.0), but bail if there are any unexpected chars
+	// Advance beyond r, u, or ur prefix (or r, b, or br in Python 2.7+ and r, f, or fr in Python 3.6+), but bail if there are any unexpected chars
 	if (ch == 'r' || ch == 'R') {
 		i++;
 		ch = styler.SafeGetCharAt(i);
@@ -96,25 +131,45 @@ int GetPyStringState(Accessor &styler, Sci_Position i, Sci_PositionU *nextIndex,
 		*nextIndex = i + 3;
 
 		if (ch == '"')
-			return SCE_P_TRIPLEDOUBLE;
+			return (firstIsF ? SCE_P_FTRIPLEDOUBLE : SCE_P_TRIPLEDOUBLE);
 		else
-			return SCE_P_TRIPLE;
+			return (firstIsF ? SCE_P_FTRIPLE : SCE_P_TRIPLE);
 	} else {
 		*nextIndex = i + 1;
 
 		if (ch == '"')
-			return SCE_P_STRING;
+			return (firstIsF ? SCE_P_FSTRING : SCE_P_STRING);
 		else
-			return SCE_P_CHARACTER;
+			return (firstIsF ? SCE_P_FCHARACTER : SCE_P_CHARACTER);
 	}
 }
 
-inline bool IsAWordChar(int ch) {
-	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
+inline bool IsAWordChar(int ch, bool unicodeIdentifiers) {
+	if (ch < 0x80)
+		return (isalnum(ch) || ch == '.' || ch == '_');
+
+	if (!unicodeIdentifiers)
+		return false;
+
+	// Approximation, Python uses the XID_Continue set from unicode data
+	// see http://unicode.org/reports/tr31/
+	CharacterCategory c = CategoriseCharacter(ch);
+	return (c == ccLl || c == ccLu || c == ccLt || c == ccLm || c == ccLo
+		|| c == ccNl || c == ccMn || c == ccMc || c == ccNd || c == ccPc);
 }
 
-inline bool IsAWordStart(int ch) {
-	return (ch < 0x80) && (isalnum(ch) || ch == '_');
+inline bool IsAWordStart(int ch, bool unicodeIdentifiers) {
+	if (ch < 0x80)
+		return (isalpha(ch) || ch == '_');
+
+	if (!unicodeIdentifiers)
+		return false;
+
+	// Approximation, Python uses the XID_Start set from unicode data
+	// see http://unicode.org/reports/tr31/
+	CharacterCategory c = CategoriseCharacter(ch);
+	return (c == ccLl || c == ccLu || c == ccLt || c == ccLm || c == ccLo
+		|| c == ccNl);
 }
 
 static bool IsFirstNonWhitespace(Sci_Position pos, Accessor &styler) {
@@ -134,28 +189,34 @@ struct OptionsPython {
 	bool base2or8Literals;
 	bool stringsU;
 	bool stringsB;
+	bool stringsF;
 	bool stringsOverNewline;
 	bool keywords2NoSubIdentifiers;
 	bool fold;
 	bool foldQuotes;
 	bool foldCompact;
+	bool unicodeIdentifiers;
 
 	OptionsPython() {
 		whingeLevel = 0;
 		base2or8Literals = true;
 		stringsU = true;
 		stringsB = true;
+		stringsF = true;
 		stringsOverNewline = false;
 		keywords2NoSubIdentifiers = false;
 		fold = false;
 		foldQuotes = false;
 		foldCompact = false;
+		unicodeIdentifiers = true;
 	}
 
 	literalsAllowed AllowedLiterals() const {
 		literalsAllowed allowedLiterals = stringsU ? litU : litNone;
 		if (stringsB)
 			allowedLiterals = static_cast<literalsAllowed>(allowedLiterals | litB);
+		if (stringsF)
+			allowedLiterals = static_cast<literalsAllowed>(allowedLiterals | litF);
 		return allowedLiterals;
 	}
 };
@@ -186,6 +247,9 @@ struct OptionSetPython : public OptionSet<OptionsPython> {
 		DefineProperty("lexer.python.strings.b", &OptionsPython::stringsB,
 			"Set to 0 to not recognise Python 3 bytes literals b\"x\".");
 
+		DefineProperty("lexer.python.strings.f", &OptionsPython::stringsF,
+			"Set to 0 to not recognise Python 3.6 f-string literals f\"var={var}\".");
+
 		DefineProperty("lexer.python.strings.over.newline", &OptionsPython::stringsOverNewline,
 			"Set to 1 to allow strings to span newline characters.");
 
@@ -200,6 +264,9 @@ struct OptionSetPython : public OptionSet<OptionsPython> {
 
 		DefineProperty("fold.compact", &OptionsPython::foldCompact);
 
+		DefineProperty("lexer.python.unicode.identifiers", &OptionsPython::unicodeIdentifiers,
+			"Set to 0 to not recognise Python 3 unicode identifiers.");
+
 		DefineWordListSets(pythonWordListDesc);
 	}
 };
@@ -284,6 +351,9 @@ class LexerPython : public ILexerWithSubStyles {
 	static ILexer *LexerFactoryPython() {
 		return new LexerPython();
 	}
+
+private:
+	void ProcessLineEnd(StyleContext &sc, int *fstringStateStack, bool &inContinuedString) const;
 };
 
 Sci_Position SCI_METHOD LexerPython::PropertySet(const char *key, const char *val) {
@@ -315,9 +385,35 @@ Sci_Position SCI_METHOD LexerPython::WordListSet(int n, const char *wl) {
 	return firstModification;
 }
 
+void LexerPython::ProcessLineEnd(StyleContext &sc, int *fstringStateStack, bool &inContinuedString) const {
+	// Restore to to outermost string state if in an f-string expression and 
+	// let code below decide what to do
+	while (fstringStateStack[0] != 0) {
+		sc.SetState(PopFromStateStack(fstringStateStack, 4));
+	}
+
+	if ((sc.state == SCE_P_DEFAULT)
+		|| IsPyTripleQuoteStringState(sc.state)) {
+		// Perform colourisation of white space and triple quoted strings at end of each line to allow
+		// tab marking to work inside white space and triple quoted strings
+		sc.SetState(sc.state);
+	}
+	if (IsPySingleQuoteStringState(sc.state)) {
+		if (inContinuedString || options.stringsOverNewline) {
+			inContinuedString = false;
+		} else {
+			sc.ChangeState(SCE_P_STRINGEOL);
+			sc.ForwardSetState(SCE_P_DEFAULT);
+		}
+	}
+}
+
 void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
 	Accessor styler(pAccess, NULL);
 
+	// Track whether in f-string expression; an array is used for a stack to
+	// handle nested f-strings such as f"""{f'''{f"{f'{1}'}"}'''}"""
+	int fstringStateStack[4] = { 0, };
 	const Sci_Position endPos = startPos + length;
 
 	// Backtrack to previous line in case need to fix its tab whinging
@@ -383,39 +479,25 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
 		}
 
 		if (sc.atLineEnd) {
-			if ((sc.state == SCE_P_DEFAULT) ||
-			        (sc.state == SCE_P_TRIPLE) ||
-			        (sc.state == SCE_P_TRIPLEDOUBLE)) {
-				// Perform colourisation of white space and triple quoted strings at end of each line to allow
-				// tab marking to work inside white space and triple quoted strings
-				sc.SetState(sc.state);
-			}
+			ProcessLineEnd(sc, fstringStateStack, inContinuedString);
 			lineCurrent++;
-			if ((sc.state == SCE_P_STRING) || (sc.state == SCE_P_CHARACTER)) {
-				if (inContinuedString || options.stringsOverNewline) {
-					inContinuedString = false;
-				} else {
-					sc.ChangeState(SCE_P_STRINGEOL);
-					sc.ForwardSetState(SCE_P_DEFAULT);
-				}
-			}
 			if (!sc.More())
 				break;
 		}
 
 		bool needEOLCheck = false;
 
-		// Check for a state end
+
 		if (sc.state == SCE_P_OPERATOR) {
 			kwLast = kwOther;
 			sc.SetState(SCE_P_DEFAULT);
 		} else if (sc.state == SCE_P_NUMBER) {
-			if (!IsAWordChar(sc.ch) &&
+			if (!IsAWordChar(sc.ch, false) &&
 			        !(!base_n_number && ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) {
 				sc.SetState(SCE_P_DEFAULT);
 			}
 		} else if (sc.state == SCE_P_IDENTIFIER) {
-			if ((sc.ch == '.') || (!IsAWordChar(sc.ch))) {
+			if ((sc.ch == '.') || (!IsAWordChar(sc.ch, options.unicodeIdentifiers))) {
 				char s[100];
 				sc.GetCurrent(s, sizeof(s));
 				int style = SCE_P_IDENTIFIER;
@@ -487,10 +569,10 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
 				sc.SetState(SCE_P_DEFAULT);
 			}
 		} else if (sc.state == SCE_P_DECORATOR) {
-			if (!IsAWordChar(sc.ch)) {
+			if (!IsAWordStart(sc.ch, options.unicodeIdentifiers)) {
 				sc.SetState(SCE_P_DEFAULT);
 			}
-		} else if ((sc.state == SCE_P_STRING) || (sc.state == SCE_P_CHARACTER)) {
+		} else if (IsPySingleQuoteStringState(sc.state)) {
 			if (sc.ch == '\\') {
 				if ((sc.chNext == '\r') && (sc.GetRelative(2) == '\n')) {
 					sc.Forward();
@@ -501,14 +583,16 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
 					// Don't roll over the newline.
 					sc.Forward();
 				}
-			} else if ((sc.state == SCE_P_STRING) && (sc.ch == '\"')) {
+			} else if (((sc.state == SCE_P_STRING || sc.state == SCE_P_FSTRING))
+				   && (sc.ch == '\"')) {
 				sc.ForwardSetState(SCE_P_DEFAULT);
 				needEOLCheck = true;
-			} else if ((sc.state == SCE_P_CHARACTER) && (sc.ch == '\'')) {
+			} else if (((sc.state == SCE_P_CHARACTER) || (sc.state == SCE_P_FCHARACTER))
+				   && (sc.ch == '\'')) {
 				sc.ForwardSetState(SCE_P_DEFAULT);
 				needEOLCheck = true;
 			}
-		} else if (sc.state == SCE_P_TRIPLE) {
+		} else if ((sc.state == SCE_P_TRIPLE) || (sc.state == SCE_P_FTRIPLE)) {
 			if (sc.ch == '\\') {
 				sc.Forward();
 			} else if (sc.Match("\'\'\'")) {
@@ -517,7 +601,7 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
 				sc.ForwardSetState(SCE_P_DEFAULT);
 				needEOLCheck = true;
 			}
-		} else if (sc.state == SCE_P_TRIPLEDOUBLE) {
+		} else if ((sc.state == SCE_P_TRIPLEDOUBLE) || (sc.state == SCE_P_FTRIPLEDOUBLE)) {
 			if (sc.ch == '\\') {
 				sc.Forward();
 			} else if (sc.Match("\"\"\"")) {
@@ -527,6 +611,19 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
 				needEOLCheck = true;
 			}
 		}
+		
+		// Note if used and not if else because string states also match
+		// some of the above clauses
+		if (IsPyFStringState(sc.state) && sc.ch == '{') {
+			if (sc.chNext == '{') {
+				sc.Forward();
+			} else {
+				PushStateToStack(sc.state, fstringStateStack, ELEMENTS(fstringStateStack));
+				sc.ForwardSetState(SCE_P_DEFAULT);
+			}
+			needEOLCheck = true;
+		}
+		// End of code to find the end of a state
 
 		if (!indentGood && !IsASpaceOrTab(sc.ch)) {
 			styler.IndicatorFill(startIndicator, sc.currentPos, indicatorWhitespace, 1);
@@ -541,12 +638,18 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
 
 		// State exit code may have moved on to end of line
 		if (needEOLCheck && sc.atLineEnd) {
+			ProcessLineEnd(sc, fstringStateStack, inContinuedString);
 			lineCurrent++;
 			styler.IndentAmount(lineCurrent, &spaceFlags, IsPyComment);
 			if (!sc.More())
 				break;
 		}
 
+		// If in f-string expression, check for } to resume f-string state
+		if (fstringStateStack[0] != 0 && sc.ch == '}') {
+			sc.SetState(PopFromStateStack(fstringStateStack, ELEMENTS(fstringStateStack)));
+		}
+
 		// Check for a new state starting character
 		if (sc.state == SCE_P_DEFAULT) {
 			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
@@ -581,7 +684,7 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
 				while (nextIndex > (sc.currentPos + 1) && sc.More()) {
 					sc.Forward();
 				}
-			} else if (IsAWordStart(sc.ch)) {
+			} else if (IsAWordStart(sc.ch, options.unicodeIdentifiers)) {
 				sc.SetState(SCE_P_IDENTIFIER);
 			}
 		}


Modified: scintilla/lexlib/WordList.cxx
10 lines changed, 3 insertions(+), 7 deletions(-)
===================================================================
@@ -29,10 +29,7 @@ static char **ArrayFromWordList(char *wordlist, int *len, bool onlyLineEnds = fa
 	int words = 0;
 	// For rapid determination of whether a character is a separator, build
 	// a look up table.
-	bool wordSeparator[256];
-	for (int i=0; i<256; i++) {
-		wordSeparator[i] = false;
-	}
+	bool wordSeparator[256] = {};	// Initialise all to false.
 	wordSeparator[static_cast<unsigned int>('\r')] = true;
 	wordSeparator[static_cast<unsigned int>('\n')] = true;
 	if (!onlyLineEnds) {
@@ -118,7 +115,7 @@ static int cmpWords(const void *a, const void *b) {
 }
 
 static void SortWordList(char **words, unsigned int len) {
-	qsort(reinterpret_cast<void *>(words), len, sizeof(*words), cmpWords);
+	qsort(static_cast<void *>(words), len, sizeof(*words), cmpWords);
 }
 
 #endif
@@ -134,8 +131,7 @@ void WordList::Set(const char *s) {
 #else
 	SortWordList(words, len);
 #endif
-	for (unsigned int k = 0; k < ELEMENTS(starts); k++)
-		starts[k] = -1;
+	std::fill(starts, starts + ELEMENTS(starts), -1);
 	for (int l = len - 1; l >= 0; l--) {
 		unsigned char indexChar = words[l][0];
 		starts[indexChar] = l;


Modified: scintilla/src/Document.cxx
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -1805,7 +1805,7 @@ bool Document::MatchesWordOptions(bool word, bool wordStart, int pos, int length
 			(wordStart && IsWordStartAt(pos));
 }
 
-bool Document::HasCaseFolder(void) const {
+bool Document::HasCaseFolder() const {
 	return pcf != 0;
 }
 


Modified: scintilla/src/Document.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -400,7 +400,7 @@ class Document : PerLine, public IDocumentWithLineEnd, public ILoader {
 	bool IsWordAt(int start, int end) const;
 
 	bool MatchesWordOptions(bool word, bool wordStart, int pos, int length) const;
-	bool HasCaseFolder(void) const;
+	bool HasCaseFolder() const;
 	void SetCaseFolder(CaseFolder *pcf_);
 	long FindText(int minPos, int maxPos, const char *search, int flags, int *length);
 	const char *SubstituteByPosition(const char *text, int *length);


Modified: scintilla/src/EditView.cxx
10 lines changed, 8 insertions(+), 2 deletions(-)
===================================================================
@@ -1288,7 +1288,13 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt
 	// For each selection draw
 	for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
 		const bool mainCaret = r == model.sel.Main();
-		const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
+		SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
+		if (vsDraw.caretStyle == CARETSTYLE_BLOCK && !drawDrag && posCaret > model.sel.Range(r).anchor) {
+			if (posCaret.VirtualSpace() > 0)
+				posCaret.SetVirtualSpace(posCaret.VirtualSpace() - 1);
+			else
+				posCaret.SetPosition(model.pdoc->MovePositionOutsideChar(posCaret.Position()-1, -1));
+		}
 		const int offset = posCaret.Position() - posLineStart;
 		const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
 		const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
@@ -2085,7 +2091,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan
 }
 
 void EditView::FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
-	int line, PRectangle rcArea, int subLine) {
+	int line, PRectangle rcArea, int subLine) const {
 		int eolInSelection = 0;
 		int alpha = SC_ALPHA_NOALPHA;
 		if (!hideSelection) {


Modified: scintilla/src/EditView.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -143,7 +143,7 @@ class EditView {
 	void PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea, PRectangle rcClient,
 		const ViewStyle &vsDraw);
 	void FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
-		int line, PRectangle rcArea, int subLine);
+		int line, PRectangle rcArea, int subLine) const;
 	long FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
 		const EditModel &model, const ViewStyle &vs);
 };


Modified: scintilla/src/Editor.cxx
6 lines changed, 3 insertions(+), 3 deletions(-)
===================================================================
@@ -2611,10 +2611,10 @@ void Editor::NotifyModified(Document *, DocModification mh, void *) {
 				if (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))
 					endNeedShown = pdoc->LineStart(lineOfPos+1);
 			} else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
-				// Extend the need shown area over any folded lines
+				// If the deletion includes any EOL then we extend the need shown area.
 				endNeedShown = mh.position + mh.length;
 				int lineLast = pdoc->LineFromPosition(mh.position+mh.length);
-				for (int line = lineOfPos; line <= lineLast; line++) {
+				for (int line = lineOfPos + 1; line <= lineLast; line++) {
 					const int lineMaxSubord = pdoc->GetLastChild(line, -1, -1);
 					if (lineLast < lineMaxSubord) {
 						lineLast = lineMaxSubord;
@@ -5500,7 +5500,7 @@ void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
 	if (!(levelNow & SC_FOLDLEVELWHITEFLAG) && (LevelNumber(levelPrev) < LevelNumber(levelNow))) {
 		if (cs.HiddenLines()) {
 			const int parentLine = pdoc->GetFoldParent(line);
-			if (!cs.GetExpanded(parentLine) && cs.GetExpanded(line))
+			if (!cs.GetExpanded(parentLine) && cs.GetVisible(line))
 				FoldLine(parentLine, SC_FOLDACTION_EXPAND);
 		}
 	}


Modified: scintilla/src/ScintillaBase.cxx
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -202,7 +202,7 @@ int ScintillaBase::KeyCommand(unsigned int iMessage) {
 }
 
 void ScintillaBase::AutoCompleteDoubleClick(void *p) {
-	ScintillaBase *sci = reinterpret_cast<ScintillaBase *>(p);
+	ScintillaBase *sci = static_cast<ScintillaBase *>(p);
 	sci->AutoCompleteCompleted(0, SC_AC_DOUBLECLICK);
 }
 


Modified: scintilla/src/XPM.cxx
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -46,7 +46,7 @@ ColourDesired XPM::ColourFromCode(int ch) const {
 	return colourCodeTable[ch];
 }
 
-void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
+void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) const {
 	if ((code != codeTransparent) && (startX != x)) {
 		PRectangle rc = PRectangle::FromInts(startX, y, x, y + 1);
 		surface->FillRectangle(rc, ColourFromCode(code));


Modified: scintilla/src/XPM.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -23,7 +23,7 @@ class XPM {
 	ColourDesired colourCodeTable[256];
 	char codeTransparent;
 	ColourDesired ColourFromCode(int ch) const;
-	void FillRun(Surface *surface, int code, int startX, int y, int x);
+	void FillRun(Surface *surface, int code, int startX, int y, int x) const;
 public:
 	explicit XPM(const char *textForm);
 	explicit XPM(const char *const *linesForm);


Modified: scintilla/version.txt
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -1 +1 @@
-372
+373


Modified: src/highlighting.c
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -1424,6 +1424,10 @@ gboolean highlighting_is_string_style(gint lexer, gint style)
 				style == SCE_P_TRIPLE ||
 				style == SCE_P_TRIPLEDOUBLE ||
 				style == SCE_P_CHARACTER ||
+				style == SCE_P_FSTRING ||
+				style == SCE_P_FCHARACTER ||
+				style == SCE_P_FTRIPLE ||
+				style == SCE_P_FTRIPLEDOUBLE ||
 				style == SCE_P_STRINGEOL);
 
 		case SCLEX_F77:


Modified: src/highlightingmappings.h
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -1257,6 +1257,10 @@ static const HLStyle highlighting_styles_PYTHON[] =
 	{ SCE_P_COMMENTBLOCK,	"commentblock",		FALSE },
 	{ SCE_P_STRINGEOL,		"stringeol",		FALSE },
 	{ SCE_P_WORD2,			"word2",			FALSE },
+	{ SCE_P_FSTRING,		"fstring",			FALSE },
+	{ SCE_P_FCHARACTER,		"fcharacter",		FALSE },
+	{ SCE_P_FTRIPLE,		"ftriple",			FALSE },
+	{ SCE_P_FTRIPLEDOUBLE,	"ftripledouble",	FALSE },
 	{ SCE_P_DECORATOR,		"decorator",		FALSE }
 };
 static const HLKeyword highlighting_keywords_PYTHON[] =



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list