Skip to content

code style #352

@tomholub

Description

@tomholub

We should have a clear code style. This will allow me to inspect the code easier. The most important goal of the code style is to increase code readability.

  1. line width 120 characters (already done)
  2. indentation 2 characters or 4 in same cases (in change code indentation to two spaces per indent #351)
  3. Variable names:
    • Use abbreviations aggressively if the meaning remains clear. For example, generalMessageDetails could be named msgDetails, as Msg is clear to mean Message. Same with Forward -> Fwd, Directory -> Dir, Reference -> Ref, Constant -> Const, Application -> App, Original -> Orig, Generate -> Gen, Command -> Cmd, Attribute -> Attr, Property -> Prop, Destination -> Dest, Connection -> Conn, Database -> Db, Credentials -> Creds, Account -> Acct, FlowCrypt -> Fc, Session -> Sess, Option -> Opt. But make sure not to shorten less common words that could be confused. For example, Connection -> Conn but leave Connect as full word. Same with Optional, etc.
    • Skip words that do not add a meaningful distinction. generalMsgDetails -> msgDetails, msgDaoSource -> msgDao
  4. English language improvements:
    • is at the beginning should generally follow with ing or ed if there is a verb at the end: isThreadAlreadyWork() -> isThreadAlreadyWorking(). But even better, isThreadBusy(). Also isDebugEnable -> isDebugEnabled
    • ifNeed should be spelled as ifNeeded.
    • WithOut should be spelled as Without, DataBase should be spelled as Database, but even better Db
  5. Use e for Exception name when catching, eg } catch (MessagingException e) {, but only if there is only one level of exception catching (no nested try/catch within each other). When you have nested catch/try blocks, give exceptions meaningful names (not e and e1!), eg:
// this is ok
try {
  callSomeApi();
} catch (MessagingException e) {
  showRetryButton(e);
}

// this is ok
try {
  try {
    Info info = getInfoFromDb();
  } catch (DbCorruptedException dbError) {
    reportToAcra(dbError);
    renderError(dbError)
    return;
  }
  callSomeApi();
} catch (MessagingException msgError) {
  renderRetryButton(msgError);
}

// this is NOT ok
try {
  try {
    Info info = getInfoFromDb();
  } catch (DbCorruptedException e) {
    reportToAcra(e);
    renderError(e)
    return;
  }
  callSomeApi();
} catch (MessagingException e1) {
  renderRetryButton(e1);
}
  1. Avoid For if that can make the name shorter. getSessionForAccountDao -> getAccountDaoSession. (It could be shortened further as getAcctDaoSess)

  2. Maximum 8 nested block indents, except when the lines are very short and simple. Create a new method and call it if nested blocks get too deep. Eg this is not good (11 indents).

    public LoaderResult loadInBackground() {
        List<KeyDetails> acceptedKeysList = new ArrayList<>();
        try {
            KeyStoreCryptoManager keyStoreCryptoManager = new KeyStoreCryptoManager(getContext());
            Js js = new Js(getContext(), null);
            for (KeyDetails keyDetails : privateKeyDetailsList) {
                String armoredPrivateKey = keyDetails.getValue();
                String normalizedArmoredKey = js.crypto_key_normalize(armoredPrivateKey);

                PgpKey pgpKey = js.crypto_key_read(normalizedArmoredKey);
                V8Object v8Object = js.crypto_key_decrypt(pgpKey, passphrase);

                if (pgpKey.isPrivate()) {
                    if (v8Object != null && v8Object.getBoolean(KEY_SUCCESS)) {
                        if (!keysDaoSource.isKeyExist(getContext(), pgpKey.getLongid())) {
                            Uri uri = keysDaoSource.addRow(getContext(),
                                    KeysDao.generateKeysDao(keyStoreCryptoManager, keyDetails, pgpKey, passphrase));

                            PgpContact[] pgpContacts = pgpKey.getUserIds();
                            List<Pair<String, String>> pairs = new ArrayList<>();
                            if (pgpContacts != null) {
                                ContactsDaoSource contactsDaoSource = new ContactsDaoSource();

                                for (PgpContact pgpContact : pgpContacts) {
                                    if (pgpContact != null) {
                                        PgpKey publicKey = pgpKey.toPublic();
                                        pgpContact.setPubkey(publicKey.armor());
                                        if (js.str_is_email_valid(pgpContact.getEmail()) &&
                                                contactsDaoSource.getPgpContact(getContext(), pgpContact.getEmail())
                                                        == null) {
                                            new ContactsDaoSource().addRow(getContext(), pgpContact);

Instead you should remove chunks and make them into methods, eg:

public void processAndSavePgpContacts(pgpContacts: PgpContact[]) {
  ContactsDaoSource contactsDao = new ContactsDaoSource();
  for (PgpContact pgpContact : pgpContacts) {
    if (pgpContact != null) {
      PgpKey publicKey = pgpKey.toPublic();
      pgpContact.setPubkey(publicKey.armor());
      String email = pgpContact.getEmail();
      if (js.str_is_email_valid(email) && contactsDao.getPgpContact(getContext(), email) == null) {
        new ContactsDaoSource().addRow(getContext(), pgpContact);
        // todo-DenBond7 Need to resolve a situation with different public keys.
        // For example we can have a situation when we have to different public
        // keys with the same email
      }
      pairs.add(Pair.create(pgpKey.getLongid(), email));
    }
  }
}

// ...

if (pgpContacts != null) {
  processAndSavePgpContacts(pgpContacts);
}
  1. Save a value into a shorter variable if it repeats in a line and it would cause the line to not wrap, especially if statements. This is bad:
      if (js.str_is_email_valid(pgpContact.getEmail()) && contactsDao.getPgpContact(getContext(), pgpContact.getEmail())
              == null) {
        new ContactsDaoSource().addRow(getContext(), pgpContact);
      }

This is good:

      String email = pgpContact.getEmail();
      if (js.str_is_email_valid(email) && contactsDao.getPgpContact(getContext(), email) == null) {
        new ContactsDaoSource().addRow(getContext(), pgpContact);
      }

The following are recommended - up to discussion - let me know what you think

  1. object properties related to count would start with lowercase n: countOfLoadedMessages -> nLoadedMessages, countOfNewMessages -> nNewMessages while properties representing a number of something (order / position in a list) would be spelled out: messageNumber is correct
    • can start with is, has, does in a way that it makes sense in English: .isFolderHasNoSelectAttribute() -> .doesFolderHaveNoSelectAttr()
    • except where negative name makes a lot of sense (should be rare), always use positive names: o.doesFolderHaveNoSelectAttr() -> !o.doesFolderHaveSelectAttr() and o.isKeyNotExistsInList() -> !o.doesListContainKey()
  2. Queue must always be named Q or q, and nothing else can be named Q or q. activeSyncTaskBlockingQueue -> activeSyncTaskBlockingQ
  3. Attachment is a frequent word that can even show several times per line with no obvious short form. We could agree to always call it Attachment -> Att and Attachments -> Atts. It's not the most obvious for 3rd party developers, but if we agree on it and are consistent, it will shorten the code considerably.

Most of these rules result in shorter code. Shorter variable, class, property, method and object names mean that more code fits on a line (limit 120), which means that the line is less likely to be wrapped at the end. I find lines that wrap around a lot hard to read. Here is an example of how this can help:

// this spans half of a line
PropertiesHelper.generatePropertiesForDownloadGmailAttachments();
// updated by using code style above
PropsHelper.genPropsForDownloadGmailAttachments();
// but even better, which some shorter wording
PropsHelper.genGmailAttachmentDownloadProps();

// another example
messageDaoSource.updateMessageState(context,
        generalMessageDetails.getEmail(), generalMessageDetails.getLabel(),
        generalMessageDetails.getUid(), MessageState.SENDING);
// becomes
msgDao.updateMsgState(context, msgDetails.getEmail(), msgDetails.getLabel(),
    msgDetails.getUid(), MsgState.SENDING);
// which is much more readable in my opinion

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions