@@ -42,6 +42,25 @@ extern char **completion_matches(char *, CPFunction *);
4242#endif
4343#endif
4444
45+ #ifdef __APPLE__
46+ /*
47+ * It is possible to link the readline module to the readline
48+ * emulation library of editline/libedit.
49+ *
50+ * On OSX this emulation library is not 100% API compatible
51+ * with the "real" readline and cannot be detected at compile-time,
52+ * hence we use a runtime check to detect if we're using libedit
53+ *
54+ * Currently there is one know API incompatibility:
55+ * - 'get_history' has a 1-based index with GNU readline, and a 0-based
56+ * index with libedit's emulation.
57+ * - Note that replace_history and remove_history use a 0-based index
58+ * with both implementation.
59+ */
60+ static int using_libedit_emulation = 0 ;
61+ static const char libedit_version_tag [] = "EditLine wrapper" ;
62+ #endif /* __APPLE__ */
63+
4564static void
4665on_completion_display_matches_hook (char * * matches ,
4766 int num_matches , int max_length );
@@ -478,6 +497,29 @@ get_history_item(PyObject *self, PyObject *args)
478497
479498 if (!PyArg_ParseTuple (args , "i:index" , & idx ))
480499 return NULL ;
500+ #ifdef __APPLE__
501+ if (using_libedit_emulation ) {
502+ /* Libedit emulation uses 0-based indexes,
503+ * the real one uses 1-based indexes,
504+ * adjust the index to ensure that Python
505+ * code doesn't have to worry about the
506+ * difference.
507+ */
508+ HISTORY_STATE * hist_st ;
509+ hist_st = history_get_history_state ();
510+
511+ idx -- ;
512+
513+ /*
514+ * Apple's readline emulation crashes when
515+ * the index is out of range, therefore
516+ * test for that and fail gracefully.
517+ */
518+ if (idx < 0 || idx >= hist_st -> length ) {
519+ Py_RETURN_NONE ;
520+ }
521+ }
522+ #endif /* __APPLE__ */
481523 if ((hist_ent = history_get (idx )))
482524 return PyUnicode_FromString (hist_ent -> line );
483525 else {
@@ -977,6 +1019,15 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
9771019 char * line ;
9781020 HISTORY_STATE * state = history_get_history_state ();
9791021 if (state -> length > 0 )
1022+ #ifdef __APPLE__
1023+ if (using_libedit_emulation ) {
1024+ /*
1025+ * Libedit's emulation uses 0-based indexes,
1026+ * the real readline uses 1-based indexes.
1027+ */
1028+ line = history_get (state -> length - 1 )-> line ;
1029+ } else
1030+ #endif /* __APPLE__ */
9801031 line = history_get (state -> length )-> line ;
9811032 else
9821033 line = "" ;
@@ -1010,6 +1061,10 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
10101061PyDoc_STRVAR (doc_module ,
10111062"Importing this module enables command line editing using GNU readline." );
10121063
1064+ #ifdef __APPLE__
1065+ PyDoc_STRVAR (doc_module_le ,
1066+ "Importing this module enables command line editing using libedit readline." );
1067+ #endif /* __APPLE__ */
10131068
10141069static struct PyModuleDef readlinemodule = {
10151070 PyModuleDef_HEAD_INIT ,
@@ -1023,15 +1078,29 @@ static struct PyModuleDef readlinemodule = {
10231078 NULL
10241079};
10251080
1081+
10261082PyMODINIT_FUNC
10271083PyInit_readline (void )
10281084{
10291085 PyObject * m ;
10301086
1087+ #ifdef __APPLE__
1088+ if (strncmp (rl_library_version , libedit_version_tag , strlen (libedit_version_tag )) == 0 ) {
1089+ using_libedit_emulation = 1 ;
1090+ }
1091+
1092+ if (using_libedit_emulation )
1093+ readlinemodule .m_doc = doc_module_le ;
1094+
1095+ #endif /* __APPLE__ */
1096+
10311097 m = PyModule_Create (& readlinemodule );
1098+
10321099 if (m == NULL )
10331100 return NULL ;
10341101
1102+
1103+
10351104 PyOS_ReadlineFunctionPointer = call_readline ;
10361105 setup_readline ();
10371106 return m ;
0 commit comments