/eclipse-pydev-2.6.0/plugins/org.python.pydev.core/src/org/python/pydev/core/docutils/ParsingUtils.java

# · Java · 1020 lines · 734 code · 119 blank · 167 comment · 127 complexity · e15f88fecb51dc01a33062fb7fa845b3 MD5 · raw file

  1. /**
  2. * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
  3. * Licensed under the terms of the Eclipse Public License (EPL).
  4. * Please see the license.txt included with this distribution for details.
  5. * Any modifications to this file must keep this entire header intact.
  6. */
  7. /*
  8. * Created on 13/07/2005
  9. */
  10. package org.python.pydev.core.docutils;
  11. import java.util.Iterator;
  12. import org.eclipse.jface.text.BadLocationException;
  13. import org.eclipse.jface.text.IDocument;
  14. import org.eclipse.jface.text.IDocumentExtension3;
  15. import org.eclipse.jface.text.IDocumentPartitionerExtension2;
  16. import org.python.pydev.core.IPythonPartitions;
  17. import org.python.pydev.core.structure.FastStringBuffer;
  18. /**
  19. * Helper class for parsing python code.
  20. *
  21. * @author Fabio
  22. */
  23. public abstract class ParsingUtils implements IPythonPartitions{
  24. private boolean throwSyntaxError;
  25. public ParsingUtils(boolean throwSyntaxError){
  26. this.throwSyntaxError = throwSyntaxError;
  27. }
  28. /**
  29. * Class that handles char[]
  30. *
  31. * @author Fabio
  32. */
  33. private static final class FixedLenCharArrayParsingUtils extends ParsingUtils{
  34. private final char[] cs;
  35. private final int len;
  36. public FixedLenCharArrayParsingUtils(char[] cs, boolean throwSyntaxError, int len) {
  37. super(throwSyntaxError);
  38. this.cs = cs;
  39. this.len = len;
  40. }
  41. public int len() {
  42. return len;
  43. }
  44. public char charAt(int i) {
  45. return cs[i];
  46. }
  47. }
  48. /**
  49. * Class that handles FastStringBuffer
  50. *
  51. * @author Fabio
  52. */
  53. private static final class FixedLenFastStringBufferParsingUtils extends ParsingUtils{
  54. private final FastStringBuffer cs;
  55. private final int len;
  56. public FixedLenFastStringBufferParsingUtils(FastStringBuffer cs, boolean throwSyntaxError, int len) {
  57. super(throwSyntaxError);
  58. this.cs = cs;
  59. this.len = len;
  60. }
  61. public int len() {
  62. return len;
  63. }
  64. public char charAt(int i) {
  65. return cs.charAt(i);
  66. }
  67. }
  68. /**
  69. * Class that handles StringBuffer
  70. *
  71. * @author Fabio
  72. */
  73. private static final class FixedLenStringBufferParsingUtils extends ParsingUtils{
  74. private final StringBuffer cs;
  75. private final int len;
  76. public FixedLenStringBufferParsingUtils(StringBuffer cs, boolean throwSyntaxError, int len) {
  77. super(throwSyntaxError);
  78. this.cs = cs;
  79. this.len = len;
  80. }
  81. public int len() {
  82. return len;
  83. }
  84. public char charAt(int i) {
  85. return cs.charAt(i);
  86. }
  87. }
  88. /**
  89. * Class that handles String
  90. *
  91. * @author Fabio
  92. */
  93. private static final class FixedLenStringParsingUtils extends ParsingUtils{
  94. private final String cs;
  95. private final int len;
  96. public FixedLenStringParsingUtils(String cs, boolean throwSyntaxError, int len) {
  97. super(throwSyntaxError);
  98. this.cs = cs;
  99. this.len = len;
  100. }
  101. public int len() {
  102. return len;
  103. }
  104. public char charAt(int i) {
  105. return cs.charAt(i);
  106. }
  107. }
  108. /**
  109. * Class that handles String
  110. *
  111. * @author Fabio
  112. */
  113. private static final class FixedLenIDocumentParsingUtils extends ParsingUtils{
  114. private final IDocument cs;
  115. private final int len;
  116. public FixedLenIDocumentParsingUtils(IDocument cs, boolean throwSyntaxError, int len) {
  117. super(throwSyntaxError);
  118. this.cs = cs;
  119. this.len = len;
  120. }
  121. public int len() {
  122. return len;
  123. }
  124. public char charAt(int i) {
  125. try {
  126. return cs.getChar(i);
  127. } catch (BadLocationException e) {
  128. throw new RuntimeException(e);
  129. }
  130. }
  131. }
  132. /**
  133. * Class that handles FastStringBuffer
  134. *
  135. * @author Fabio
  136. */
  137. private static final class FastStringBufferParsingUtils extends ParsingUtils{
  138. private final FastStringBuffer cs;
  139. public FastStringBufferParsingUtils(FastStringBuffer cs, boolean throwSyntaxError) {
  140. super(throwSyntaxError);
  141. this.cs = cs;
  142. }
  143. public int len() {
  144. return cs.length();
  145. }
  146. public char charAt(int i) {
  147. return cs.charAt(i);
  148. }
  149. }
  150. /**
  151. * Class that handles StringBuffer
  152. *
  153. * @author Fabio
  154. */
  155. private static final class StringBufferParsingUtils extends ParsingUtils{
  156. private final StringBuffer cs;
  157. public StringBufferParsingUtils(StringBuffer cs, boolean throwSyntaxError) {
  158. super(throwSyntaxError);
  159. this.cs = cs;
  160. }
  161. public int len() {
  162. return cs.length();
  163. }
  164. public char charAt(int i) {
  165. return cs.charAt(i);
  166. }
  167. }
  168. /**
  169. * Class that handles String
  170. *
  171. * @author Fabio
  172. */
  173. private static final class IDocumentParsingUtils extends ParsingUtils{
  174. private final IDocument cs;
  175. public IDocumentParsingUtils(IDocument cs, boolean throwSyntaxError) {
  176. super(throwSyntaxError);
  177. this.cs = cs;
  178. }
  179. public int len() {
  180. return cs.getLength();
  181. }
  182. public char charAt(int i) {
  183. try {
  184. return cs.getChar(i);
  185. } catch (BadLocationException e) {
  186. throw new RuntimeException(e);
  187. }
  188. }
  189. }
  190. /**
  191. * Factory method to create it (and by default doesn't throw any errors).
  192. */
  193. public static ParsingUtils create(Object cs) {
  194. return create(cs, false);
  195. }
  196. /**
  197. * Factory method to create it. Object len may not be changed afterwards.
  198. */
  199. public static ParsingUtils create(Object cs, boolean throwSyntaxError, int len) {
  200. if(cs instanceof char[]){
  201. char[] cs2 = (char[])cs;
  202. return new FixedLenCharArrayParsingUtils(cs2, throwSyntaxError, len);
  203. }
  204. if(cs instanceof FastStringBuffer){
  205. FastStringBuffer cs2 = (FastStringBuffer)cs;
  206. return new FixedLenFastStringBufferParsingUtils(cs2, throwSyntaxError, len);
  207. }
  208. if(cs instanceof StringBuffer){
  209. StringBuffer cs2 = (StringBuffer)cs;
  210. return new FixedLenStringBufferParsingUtils(cs2, throwSyntaxError, len);
  211. }
  212. if(cs instanceof String){
  213. String cs2 = (String)cs;
  214. return new FixedLenStringParsingUtils(cs2, throwSyntaxError, len);
  215. }
  216. if(cs instanceof IDocument){
  217. IDocument cs2 = (IDocument)cs;
  218. return new FixedLenIDocumentParsingUtils(cs2, throwSyntaxError, len);
  219. }
  220. throw new RuntimeException("Don't know how to create instance for: "+cs.getClass());
  221. }
  222. /**
  223. * Factory method to create it.
  224. */
  225. public static ParsingUtils create(Object cs, boolean throwSyntaxError) {
  226. if(cs instanceof char[]){
  227. char[] cs2 = (char[])cs;
  228. return new FixedLenCharArrayParsingUtils(cs2, throwSyntaxError, cs2.length);
  229. }
  230. if(cs instanceof FastStringBuffer){
  231. FastStringBuffer cs2 = (FastStringBuffer)cs;
  232. return new FastStringBufferParsingUtils(cs2, throwSyntaxError);
  233. }
  234. if(cs instanceof StringBuffer){
  235. StringBuffer cs2 = (StringBuffer)cs;
  236. return new StringBufferParsingUtils(cs2, throwSyntaxError);
  237. }
  238. if(cs instanceof String){
  239. String cs2 = (String)cs;
  240. return new FixedLenStringParsingUtils(cs2, throwSyntaxError, cs2.length());
  241. }
  242. if(cs instanceof IDocument){
  243. IDocument cs2 = (IDocument)cs;
  244. return new IDocumentParsingUtils(cs2, throwSyntaxError);
  245. }
  246. throw new RuntimeException("Don't know how to create instance for: "+cs.getClass());
  247. }
  248. //Abstract interfaces -------------------------------------------------------------
  249. /**
  250. * @return the char at a given position of the object
  251. */
  252. public abstract char charAt(int i);
  253. /**
  254. * @return the length of the contained object
  255. */
  256. public abstract int len();
  257. //API methods --------------------------------------------------------------------
  258. /**
  259. * @param buf used to add the comments contents (out) -- if it's null, it'll simply advance to the position and
  260. * return it.
  261. * @param i the # position
  262. * @return the end of the comments position (end of document or new line char)
  263. * @note the new line char (\r or \n) will be added as a part of the comment.
  264. */
  265. public int eatComments(FastStringBuffer buf, int i) {
  266. int len = len();
  267. char c;
  268. while(i < len && (c = charAt(i)) != '\n' && c != '\r'){
  269. if(buf != null){
  270. buf.append(c);
  271. }
  272. i++;
  273. }
  274. if(i < len){
  275. if(buf != null){
  276. buf.append(charAt(i));
  277. }
  278. }
  279. return i;
  280. }
  281. /**
  282. * @param buf used to add the spaces (out) -- if it's null, it'll simply advance to the position and
  283. * return it.
  284. * @param i the first ' ' position
  285. * @return the position of the last space found
  286. */
  287. public int eatWhitespaces(FastStringBuffer buf, int i) {
  288. int len = len();
  289. char c;
  290. while(i < len && (c = charAt(i)) == ' '){
  291. if(buf != null){
  292. buf.append(c);
  293. }
  294. i++;
  295. }
  296. //go back to the last space found
  297. i--;
  298. return i;
  299. }
  300. public int eatLiteralsBackwards(FastStringBuffer buf, int i) throws SyntaxErrorException{
  301. //ok, current pos is ' or "
  302. //check if we're starting a single or multiline comment...
  303. char curr = charAt(i);
  304. if(curr != '"' && curr != '\''){
  305. throw new RuntimeException("Wrong location to eat literals. Expecting ' or \" Found:"+curr);
  306. }
  307. int j = getLiteralStart(i, curr);
  308. if(buf != null){
  309. for (int k = j; k <= i; k++) {
  310. buf.append(charAt(k));
  311. }
  312. }
  313. return j;
  314. }
  315. /**
  316. * Equivalent to eatLiterals(buf, startPos, false) .
  317. *
  318. * @param buf
  319. * @param startPos
  320. * @return
  321. * @throws SyntaxErrorException
  322. */
  323. public int eatLiterals(FastStringBuffer buf, int startPos)
  324. throws SyntaxErrorException {
  325. return eatLiterals(buf, startPos, false);
  326. }
  327. /**
  328. * Returns the index of the last character of the current string literal
  329. * beginning at startPos, optionally copying the contents of the literal to
  330. * an output buffer.
  331. *
  332. * @param buf
  333. * If non-null, the contents of the literal are appended to this
  334. * object.
  335. * @param startPos
  336. * The position of the initial ' or "
  337. * @param rightTrimMultiline
  338. * Whether to right trim the whitespace of each line in multi-
  339. * line literals when appending to buf .
  340. * @return The position of the last ' or " character of the literal (or the
  341. * end of the document).
  342. */
  343. public int eatLiterals(FastStringBuffer buf, int startPos,
  344. boolean rightTrimMultiline)
  345. throws SyntaxErrorException {
  346. char startChar = charAt(startPos);
  347. if (startChar != '"' && startChar != '\'') {
  348. throw new RuntimeException(
  349. "Wrong location to eat literals. Expecting ' or \" ");
  350. }
  351. // Retrieves the correct end position for single- and multi-line
  352. // string literals.
  353. int endPos = getLiteralEnd(startPos, startChar);
  354. boolean rightTrim = rightTrimMultiline
  355. && isMultiLiteral(startPos, startChar);
  356. if (buf != null) {
  357. int lastPos = Math.min(endPos, len() - 1);
  358. for (int i = startPos; i <= lastPos; i++) {
  359. char ch = charAt(i);
  360. if (rightTrim && (ch == '\r' || ch == '\n')) {
  361. buf.rightTrim();
  362. }
  363. buf.append(ch);
  364. }
  365. }
  366. return endPos;
  367. }
  368. /**
  369. * @param i index we are analyzing it
  370. * @param curr current char
  371. * @return the end of the multiline literal
  372. * @throws SyntaxErrorException
  373. */
  374. public int getLiteralStart(int i, char curr) throws SyntaxErrorException {
  375. boolean multi = isMultiLiteralBackwards(i, curr);
  376. int j;
  377. if(multi){
  378. j = findPreviousMulti(i-3, curr);
  379. }else{
  380. j = findPreviousSingle(i-1, curr);
  381. }
  382. return j;
  383. }
  384. /**
  385. * @param i index we are analyzing it
  386. * @param curr current char
  387. * @return the end of the multiline literal
  388. * @throws SyntaxErrorException
  389. */
  390. public int getLiteralEnd(int i, char curr) throws SyntaxErrorException {
  391. boolean multi = isMultiLiteral(i, curr);
  392. int j;
  393. if(multi){
  394. j = findNextMulti(i+3, curr);
  395. }else{
  396. j = findNextSingle(i+1, curr);
  397. }
  398. return j;
  399. }
  400. /**
  401. * @param i the ' or " position
  402. * @param buf used to add the comments contents (out)
  403. * @return the end of the literal position (or end of document)
  404. * @throws SyntaxErrorException
  405. */
  406. public int eatPar(int i, FastStringBuffer buf) throws SyntaxErrorException {
  407. return eatPar(i, buf, '(');
  408. }
  409. /**
  410. * @param i the index where we should start getting chars
  411. * @param buf the buffer that should be filled with the contents gotten (if null, they're ignored)
  412. * @return the index where the parsing stopped. It should always be the character just before the new line
  413. * (or before the end of the document).
  414. * @throws SyntaxErrorException
  415. */
  416. public int getFullFlattenedLine(int i, FastStringBuffer buf) throws SyntaxErrorException {
  417. char c = this.charAt(i);
  418. int len = len();
  419. boolean ignoreNextNewLine = false;
  420. while(i < len){
  421. c = charAt(i);
  422. i++;
  423. if(c == '\'' || c == '"'){ //ignore comments or multiline comments...
  424. i = eatLiterals(null, i-1)+1;
  425. }else if(c == '#'){
  426. i = eatComments(null, i-1);
  427. break;
  428. }else if( c == '(' || c == '[' || c == '{'){ //open par.
  429. i = eatPar(i-1, null, c)+1;
  430. }else if( c == '\r' || c == '\n' ){
  431. if(!ignoreNextNewLine){
  432. i--;
  433. break;
  434. }
  435. }else if( c == '\\' || c == '\\' ){
  436. ignoreNextNewLine = true;
  437. continue;
  438. }else{
  439. if(buf != null){
  440. buf.append(c);
  441. }
  442. }
  443. ignoreNextNewLine = false;
  444. }
  445. i--; //we have to do that because we passed 1 char in the beggining of the while.
  446. return i;
  447. }
  448. /**
  449. * @param buf if null, it'll simply advance without adding anything to the buffer.
  450. * @throws SyntaxErrorException
  451. */
  452. public int eatPar(int i, FastStringBuffer buf, char par) throws SyntaxErrorException {
  453. char c = ' ';
  454. char closingPar = StringUtils.getPeer(par);
  455. int j = i+1;
  456. int len = len();
  457. while(j < len && (c = charAt(j)) != closingPar){
  458. j++;
  459. if(c == '\'' || c == '"'){ //ignore comments or multiline comments...
  460. j = eatLiterals(null, j-1)+1;
  461. }else if(c == '#'){
  462. j = eatComments(null, j-1)+1;
  463. }else if( c == par){ //open another par.
  464. j = eatPar(j-1, null, par)+1;
  465. }else{
  466. if(buf != null){
  467. buf.append(c);
  468. }
  469. }
  470. }
  471. if(this.throwSyntaxError && c != closingPar){
  472. throw new SyntaxErrorException();
  473. }
  474. return j;
  475. }
  476. /**
  477. * discover the position of the closing quote
  478. * @throws SyntaxErrorException
  479. */
  480. public int findNextSingle(int i, char curr) throws SyntaxErrorException {
  481. boolean ignoreNext = false;
  482. int len = len();
  483. while(i < len){
  484. char c = charAt(i);
  485. if(!ignoreNext && c == curr){
  486. return i;
  487. }
  488. if(!ignoreNext){
  489. if(c == '\\'){ //escaped quote, ignore the next char even if it is a ' or "
  490. ignoreNext = true;
  491. }
  492. }else{
  493. ignoreNext = false;
  494. }
  495. i++;
  496. }
  497. if(throwSyntaxError){
  498. throw new SyntaxErrorException();
  499. }
  500. return i;
  501. }
  502. /**
  503. * discover the position of the closing quote
  504. * @throws SyntaxErrorException
  505. */
  506. public int findPreviousSingle(int i, char curr) throws SyntaxErrorException {
  507. while(i >= 0){
  508. char c = charAt(i);
  509. if(c == curr){
  510. if(i > 0){
  511. if(charAt(i-1) == '\\'){
  512. //escaped
  513. i--;
  514. continue;
  515. }
  516. }
  517. return i;
  518. }
  519. i--;
  520. }
  521. if(throwSyntaxError){
  522. throw new SyntaxErrorException();
  523. }
  524. return i;
  525. }
  526. /**
  527. * check the end of the multiline quote
  528. * @throws SyntaxErrorException
  529. */
  530. public int findNextMulti(int i, char curr) throws SyntaxErrorException {
  531. int len = len();
  532. while(i+2 < len){
  533. char c = charAt(i);
  534. if (c == curr && charAt(i+1) == curr && charAt(i+2) == curr){
  535. return i+2;
  536. }
  537. i++;
  538. if(c == '\\'){ //this is for escaped quotes
  539. i++;
  540. }
  541. }
  542. if(throwSyntaxError){
  543. throw new SyntaxErrorException();
  544. }
  545. if(len < i+2){
  546. return len;
  547. }
  548. return i+2;
  549. }
  550. /**
  551. * check the end of the multiline quote
  552. * @throws SyntaxErrorException
  553. */
  554. public int findPreviousMulti(int i, char curr) throws SyntaxErrorException {
  555. while(i-2 >= 0){
  556. char c = charAt(i);
  557. if (c == curr && charAt(i-1) == curr && charAt(i-2) == curr){
  558. return i-2;
  559. }
  560. i--;
  561. }
  562. if(throwSyntaxError){
  563. throw new SyntaxErrorException();
  564. }
  565. //Got to the start.
  566. return 0;
  567. }
  568. //STATIC INTERFACES FROM NOW ON ----------------------------------------------------------------
  569. //STATIC INTERFACES FROM NOW ON ----------------------------------------------------------------
  570. //STATIC INTERFACES FROM NOW ON ----------------------------------------------------------------
  571. //STATIC INTERFACES FROM NOW ON ----------------------------------------------------------------
  572. //STATIC INTERFACES FROM NOW ON ----------------------------------------------------------------
  573. /**
  574. * @param i current position (should have a ' or ")
  575. * @param curr the current char (' or ")
  576. * @return whether we are at the end of a multi line literal or not.
  577. */
  578. public boolean isMultiLiteralBackwards(int i, char curr){
  579. if(0 > i - 2){
  580. return false;
  581. }
  582. if(charAt(i-1) == curr && charAt(i-2) == curr){
  583. return true;
  584. }
  585. return false;
  586. }
  587. /**
  588. * @param i current position (should have a ' or ")
  589. * @param curr the current char (' or ")
  590. * @return whether we are at the start of a multi line literal or not.
  591. */
  592. public boolean isMultiLiteral(int i, char curr){
  593. int len = len();
  594. if(len <= i + 2){
  595. return false;
  596. }
  597. if(charAt(i+1) == curr && charAt(i+2) == curr){
  598. return true;
  599. }
  600. return false;
  601. }
  602. public static void removeCommentsWhitespacesAndLiterals(FastStringBuffer buf, boolean throwSyntaxError) throws SyntaxErrorException {
  603. removeCommentsWhitespacesAndLiterals(buf, true, throwSyntaxError);
  604. }
  605. /**
  606. * Removes all the comments, whitespaces and literals from a FastStringBuffer (might be useful when
  607. * just finding matches for something).
  608. *
  609. * NOTE: the literals and the comments are changed for spaces (if we don't remove them too)
  610. *
  611. * @param buf the buffer from where things should be removed.
  612. * @param whitespacesToo: are you sure about the whitespaces?
  613. * @throws SyntaxErrorException
  614. */
  615. public static void removeCommentsWhitespacesAndLiterals(FastStringBuffer buf, boolean whitespacesToo, boolean throwSyntaxError) throws SyntaxErrorException {
  616. ParsingUtils parsingUtils = create(buf, throwSyntaxError);
  617. for (int i = 0; i < buf.length(); i++) { //The length can'n be extracted at this point as the buffer may change its size.
  618. char ch = buf.charAt(i);
  619. if(ch == '#'){
  620. int j = i;
  621. int len = buf.length();
  622. while(j < len && ch != '\n' && ch != '\r'){
  623. ch = buf.charAt(j);
  624. j++;
  625. }
  626. buf.delete(i, j);
  627. i--;
  628. }else if(ch == '\'' || ch == '"'){
  629. int j = parsingUtils.getLiteralEnd(i, ch);
  630. if(whitespacesToo){
  631. buf.delete(i, j+1);
  632. }else{
  633. for (int k = 0; i+k < j+1; k++) {
  634. buf.replace(i+k, i+k+1, " ");
  635. }
  636. }
  637. }
  638. }
  639. if(whitespacesToo){
  640. buf.removeWhitespaces();
  641. }
  642. }
  643. public static void removeLiterals(FastStringBuffer buf, boolean throwSyntaxError) throws SyntaxErrorException {
  644. ParsingUtils parsingUtils = create(buf, throwSyntaxError);
  645. for (int i = 0; i < buf.length(); i++) {
  646. char ch = buf.charAt(i);
  647. if(ch == '#'){
  648. //just past through comments
  649. while(i < buf.length() && ch != '\n' && ch != '\r'){
  650. ch = buf.charAt(i);
  651. i++;
  652. }
  653. }
  654. if(ch == '\'' || ch == '"'){
  655. int j = parsingUtils.getLiteralEnd(i, ch);
  656. for (int k = 0; i+k < j+1; k++) {
  657. buf.replace(i+k, i+k+1, " ");
  658. }
  659. }
  660. }
  661. }
  662. public static Iterator<String> getNoLiteralsOrCommentsIterator(IDocument doc) {
  663. return new PyDocIterator(doc);
  664. }
  665. public static void removeCommentsAndWhitespaces(FastStringBuffer buf) {
  666. for (int i = 0; i < buf.length(); i++) {
  667. char ch = buf.charAt(i);
  668. if(ch == '#'){
  669. int j = i;
  670. while(j < buf.length() -1 && ch != '\n' && ch != '\r'){
  671. j++;
  672. ch = buf.charAt(j);
  673. }
  674. buf.delete(i, j);
  675. }
  676. }
  677. int length = buf.length();
  678. for (int i = length -1; i >= 0; i--) {
  679. char ch = buf.charAt(i);
  680. if(Character.isWhitespace(ch)){
  681. buf.deleteCharAt(i);
  682. }
  683. }
  684. }
  685. /**
  686. * @param initial the document
  687. * @param currPos the offset we're interested in
  688. * @return the content type of the current position
  689. *
  690. * The version with the IDocument as a parameter should be preffered, as
  691. * this one can be much slower (still, it is an alternative in tests or
  692. * other places that do not have document access), but keep in mind
  693. * that it may be slow.
  694. */
  695. public static String getContentType(String initial, int currPos) {
  696. FastStringBuffer buf = new FastStringBuffer(initial, 0);
  697. ParsingUtils parsingUtils = create(initial);
  698. String curr = PY_DEFAULT;
  699. for (int i = 0; i < buf.length() && i < currPos; i++) {
  700. char ch = buf.charAt(i);
  701. curr = PY_DEFAULT;
  702. if(ch == '#'){
  703. curr = PY_COMMENT;
  704. int j = i;
  705. while(j < buf.length()-1 && ch != '\n' && ch != '\r'){
  706. j++;
  707. ch = buf.charAt(j);
  708. }
  709. i = j;
  710. }
  711. if(i >= currPos){
  712. return curr;
  713. }
  714. if(ch == '\'' || ch == '"'){
  715. boolean multi = parsingUtils.isMultiLiteral(i, ch);
  716. if(multi){
  717. curr = PY_MULTILINE_STRING1;
  718. if(ch == '"'){
  719. curr = PY_MULTILINE_STRING2;
  720. }
  721. }else{
  722. curr = PY_SINGLELINE_STRING1;
  723. if(ch == '"'){
  724. curr = PY_SINGLELINE_STRING2;
  725. }
  726. }
  727. try{
  728. if(multi){
  729. i = parsingUtils.findNextMulti(i+3, ch);
  730. }else{
  731. i = parsingUtils.findNextSingle(i+1, ch);
  732. }
  733. }catch(SyntaxErrorException e){
  734. throw new RuntimeException(e);
  735. }
  736. if(currPos < i){
  737. return curr; //found inside
  738. }
  739. if(currPos == i){
  740. if(PY_SINGLELINE_STRING1.equals(curr) || PY_SINGLELINE_STRING2.equals(curr)){
  741. return curr;
  742. }
  743. }
  744. //if currPos == i, this means it'll go to the next partition (we always prefer open
  745. //partitions here, so, the last >>'<< from a string is actually treated as the start
  746. //of the next partition).
  747. curr = PY_DEFAULT;
  748. }
  749. }
  750. return curr;
  751. }
  752. /**
  753. * @param document the document we want to get info on
  754. * @param i the document offset we're interested in
  755. * @return the content type at that position (according to IPythonPartitions)
  756. *
  757. * Uses the default if the partitioner is not set in the document (for testing purposes)
  758. */
  759. public static String getContentType(IDocument document, int i) {
  760. IDocumentExtension3 docExtension= (IDocumentExtension3) document;
  761. IDocumentPartitionerExtension2 partitioner = (IDocumentPartitionerExtension2)
  762. docExtension.getDocumentPartitioner(IPythonPartitions.PYTHON_PARTITION_TYPE);
  763. if(partitioner != null){
  764. return partitioner.getContentType(i, true);
  765. }
  766. return getContentType(document.get(), i);
  767. }
  768. public static String makePythonParseable(String code, String delimiter) {
  769. return makePythonParseable(code, delimiter, new FastStringBuffer());
  770. }
  771. /**
  772. * Ok, this method will get some code and make it suitable for putting at a shell
  773. * @param code the initial code we'll make parseable
  774. * @param delimiter the delimiter we should use
  775. * @return a String that can be passed to the shell
  776. */
  777. public static String makePythonParseable(String code, String delimiter, FastStringBuffer lastLine) {
  778. FastStringBuffer buffer = new FastStringBuffer();
  779. FastStringBuffer currLine = new FastStringBuffer();
  780. //we may have line breaks with \r\n, or only \n or \r
  781. boolean foundNewLine = false;
  782. boolean foundNewLineAtChar;
  783. boolean lastWasNewLine = false;
  784. if(lastLine.length() > 0){
  785. lastWasNewLine = true;
  786. }
  787. for (int i = 0; i < code.length(); i++) {
  788. foundNewLineAtChar = false;
  789. char c = code.charAt(i);
  790. if(c == '\r'){
  791. if(i +1 < code.length() && code.charAt(i+1) == '\n'){
  792. i++; //skip the \n
  793. }
  794. foundNewLineAtChar = true;
  795. }else if(c == '\n'){
  796. foundNewLineAtChar = true;
  797. }
  798. if(!foundNewLineAtChar){
  799. if(lastWasNewLine && !Character.isWhitespace(c)){
  800. if(lastLine.length() > 0 && Character.isWhitespace(lastLine.charAt(0))){
  801. buffer.append(delimiter);
  802. }
  803. }
  804. currLine.append(c);
  805. lastWasNewLine = false;
  806. }else{
  807. lastWasNewLine = true;
  808. }
  809. if(foundNewLineAtChar || i == code.length()-1){
  810. if(!PySelection.containsOnlyWhitespaces(currLine.toString())){
  811. buffer.append(currLine);
  812. lastLine = currLine;
  813. currLine = new FastStringBuffer();
  814. buffer.append(delimiter);
  815. foundNewLine = true;
  816. }else{ //found a line only with whitespaces
  817. currLine = new FastStringBuffer();
  818. }
  819. }
  820. }
  821. if(!foundNewLine){
  822. buffer.append(delimiter);
  823. }else{
  824. if(!StringUtils.endsWith(buffer, '\r') && !StringUtils.endsWith(buffer, '\n')){
  825. buffer.append(delimiter);
  826. }
  827. if(lastLine.length() > 0 && Character.isWhitespace(lastLine.charAt(0)) &&
  828. (code.indexOf('\r') != -1 || code.indexOf('\n') != -1)){
  829. buffer.append(delimiter);
  830. }
  831. }
  832. return buffer.toString();
  833. }
  834. public static String removeComments(String line) {
  835. int i = line.indexOf('#');
  836. if(i != -1){
  837. return line.substring(0, i);
  838. }
  839. return line;
  840. }
  841. public static boolean isStringPartition(IDocument document, int offset) {
  842. String contentType = getContentType(document, offset);
  843. return IPythonPartitions.PY_MULTILINE_STRING1.equals(contentType)
  844. || IPythonPartitions.PY_MULTILINE_STRING2.equals(contentType)
  845. || IPythonPartitions.PY_SINGLELINE_STRING1.equals(contentType)
  846. || IPythonPartitions.PY_SINGLELINE_STRING2.equals(contentType)
  847. ;
  848. }
  849. /**
  850. * Finds the next char that matches the passed char. If not found, returns -1.
  851. */
  852. public int findNextChar(int offset, char findChar) {
  853. char c;
  854. int l = len();
  855. for(int i=offset;i<l;i++){
  856. c = charAt(i);
  857. if(c == findChar){
  858. return i;
  859. }
  860. }
  861. return -1;
  862. }
  863. }