Branch: refs/heads/master Author: Colomban Wendling ban@herbesfolles.org Committer: Colomban Wendling ban@herbesfolles.org Date: Sun, 26 Feb 2017 19:24:13 UTC Commit: b07c8b01c08e0f85b0122211129c952b2f22aecb https://github.com/geany/geany/commit/b07c8b01c08e0f85b0122211129c952b2f22ae...
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@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).